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 struct flock filelock;
480 filelock.l_type = F_RDLCK;
481 filelock.l_whence = SEEK_SET;
482 filelock.l_start = 0;
484 if( fcntl( mFileDescriptor, F_SETLKW, &filelock ) == -1 )
487 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
497 DALI_LOG_ERROR( "### Invalid fd ###\n" );
503 void Indicator::LockFile::Unlock()
505 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
507 struct flock filelock;
509 filelock.l_type = F_UNLCK;
510 filelock.l_whence = SEEK_SET;
511 filelock.l_start = 0;
513 if (fcntl(mFileDescriptor, F_SETLKW, &filelock) == -1)
516 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
520 bool Indicator::LockFile::RetrieveAndClearErrorStatus()
522 bool error = mErrorThrown;
523 mErrorThrown = false;
527 Indicator::ScopedLock::ScopedLock(LockFile* lockFile)
528 : mLockFile(lockFile),
533 mLocked = mLockFile->Lock();
537 Indicator::ScopedLock::~ScopedLock()
545 bool Indicator::ScopedLock::IsLocked()
550 Indicator::Indicator( Adaptor* adaptor, Dali::Window::WindowOrientation orientation, IndicatorInterface::Observer* observer )
552 mGestureDeltaY( 0.0f ),
553 mGestureDetected( false ),
555 mOpacityMode( Dali::Window::OPAQUE ),
556 mState( DISCONNECTED ),
558 mServerConnection( NULL ),
559 mObserver( observer ),
560 mOrientation( orientation ),
563 mVisible( Dali::Window::INVISIBLE ),
565 mIsAnimationPlaying( false ),
566 mCurrentSharedFile( 0 ),
567 mSharedBufferType( BUFFER_TYPE_SHM ),
569 mBackgroundVisible( false ),
572 mIndicatorContentActor = Dali::Actor::New();
573 mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
574 mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
576 // Indicator image handles the touch event including "leave"
577 mIndicatorContentActor.SetLeaveRequired( true );
578 mIndicatorContentActor.TouchedSignal().Connect( this, &Indicator::OnTouched );
579 mIndicatorContentActor.SetColor( Color::BLACK );
581 mIndicatorActor = Dali::Actor::New();
582 mIndicatorActor.Add( mIndicatorContentActor );
584 // Event handler to find out flick down gesture
585 mEventActor = Dali::Actor::New();
586 mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
587 mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
588 mIndicatorActor.Add( mEventActor );
590 // Attach pan gesture to find flick down during hiding.
591 // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
592 // since it consumes the pan gesture in advance.
593 mPanDetector = Dali::PanGestureDetector::New();
594 mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
595 mPanDetector.Attach( mEventActor );
599 // register indicator to accessibility adaptor
600 Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
601 if(accessibilityAdaptor)
603 AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
605 // hide the indicator by default
606 mIndicatorActor.SetVisible( false );
608 // create impl to handle ecore event
609 mImpl = new Impl(this);
612 Indicator::~Indicator()
622 mEventActor.TouchedSignal().Disconnect( this, &Indicator::OnTouched );
627 void Indicator::SetAdaptor(Adaptor* adaptor)
630 mIndicatorBuffer->SetAdaptor( adaptor );
633 Dali::Actor Indicator::GetActor()
635 return mIndicatorActor;
638 void Indicator::Open( Dali::Window::WindowOrientation orientation )
640 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
642 // Calls from Window should be set up to ensure we are in a
643 // disconnected state before opening a second time.
644 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
646 mOrientation = orientation;
650 // Change background visibility depending on orientation
651 if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
653 if( mBackgroundRenderer )
655 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
656 mBackgroundVisible = false;
661 SetOpacityMode( mOpacityMode );
665 void Indicator::Close()
667 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
669 if( mState == CONNECTED )
672 if( mObserver != NULL )
674 mObserver->IndicatorClosed( this );
678 Dali::Texture emptyTexture;
679 SetForegroundImage( emptyTexture );
682 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
686 Dali::Geometry geometry = CreateBackgroundGeometry();
689 if( mBackgroundRenderer )
691 if( mBackgroundRenderer.GetGeometry() != geometry )
693 mBackgroundRenderer.SetGeometry( geometry );
698 if( !mBackgroundShader )
700 mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT );
703 mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
706 if( !mBackgroundVisible )
708 mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
709 mBackgroundVisible = true;
712 else if( mBackgroundRenderer )
714 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
715 mBackgroundVisible = false;
720 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
722 if ( visibleMode != mVisible || forceUpdate )
724 // If we were previously hidden, then we should update the image data before we display the indicator
725 if ( mVisible == Dali::Window::INVISIBLE )
727 UpdateImageData( mCurrentSharedFile );
730 if ( visibleMode == Dali::Window::INVISIBLE )
732 if (mServerConnection)
734 mServerConnection->SendEvent( OP_HIDE, NULL, 0 );
739 mIndicatorActor.SetVisible( true );
741 if( mServerConnection )
743 mServerConnection->SendEvent( OP_SHOW, NULL, 0 );
747 mVisible = visibleMode;
750 if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetTexture( 0u ) )
752 if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
755 ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
757 else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
760 ShowIndicator( KEEP_SHOWING );
765 ShowIndicator( HIDE_NOW );
775 bool Indicator::IsConnected()
777 return ( mState == CONNECTED );
780 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
784 return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
792 bool Indicator::OnTouched(Dali::Actor indicator, const Dali::TouchEvent& touchEvent)
794 if( mServerConnection )
796 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
798 // Send touch event to indicator server when indicator is showing
799 if( CheckVisibleState() || mIsShowing )
801 switch( touchPoint.state )
803 case Dali::PointState::DOWN:
805 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
806 IpcDataEvMouseDown ipcDown( touchEvent.time );
807 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
808 mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
810 if( mVisible == Dali::Window::AUTO )
812 // Stop hiding indicator
813 ShowIndicator( KEEP_SHOWING );
818 case Dali::PointState::MOTION:
820 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
821 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
825 case Dali::PointState::UP:
826 case Dali::PointState::INTERRUPTED:
828 IpcDataEvMouseUp ipcUp( touchEvent.time );
829 mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
831 if( mVisible == Dali::Window::AUTO )
834 ShowIndicator( 0.5f /* hide after 0.5 sec */ );
839 case Dali::TouchPoint::Leave:
841 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
842 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
843 IpcDataEvMouseUp ipcOut( touchEvent.time );
844 mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
859 bool Indicator::Connect()
861 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
863 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
865 bool connected = false;
867 mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
868 if( mServerConnection )
870 connected = mServerConnection->IsConnected();
873 delete mServerConnection;
874 mServerConnection = NULL;
880 StartReconnectionTimer();
890 void Indicator::StartReconnectionTimer()
892 if( ! mReconnectTimer )
894 mReconnectTimer = Dali::Timer::New(1000);
895 mConnection.DisconnectAll();
896 mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
898 mReconnectTimer.Start();
901 bool Indicator::OnReconnectTimer()
905 if( mState == DISCONNECTED )
916 void Indicator::Disconnect()
918 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
920 mState = DISCONNECTED;
922 delete mServerConnection;
923 mServerConnection = NULL;
925 ClearSharedFileInfo();
928 void Indicator::Resize( int width, int height )
939 if( mImageWidth != width || mImageHeight != height )
942 mImageHeight = height;
944 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
945 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
946 mEventActor.SetSize(mImageWidth, mImageHeight);
951 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
953 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
955 // epcEvent->ref == w
956 // epcEvent->ref_to == h
957 // epcEvent->response == buffer num
958 // epcEvent->data = lockfile + nul byte
960 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
961 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
963 int n = epcEvent->response;
965 if( n >= 0 && n < SHARED_FILE_NUMBER )
967 mCurrentSharedFile = n;
969 mSharedFileInfo[n].mImageWidth = epcEvent->ref;
970 mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
972 mSharedFileInfo[n].mLockFileName.clear();
974 mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
976 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
977 n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
982 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
984 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
986 // epcEvent->ref == shm id
987 // epcEvent->ref_to == shm num
988 // epcEvent->response == buffer num
989 // epcEvent->data = shm ref string + nul byte
991 if ( (epcEvent->data) &&
992 (epcEvent->size > 0) &&
993 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
995 int n = epcEvent->response;
997 if( n >= 0 && n < SHARED_FILE_NUMBER )
999 mCurrentSharedFile = n;
1001 mSharedFileInfo[n].mSharedFileName.clear();
1003 mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
1005 mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
1006 mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
1008 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
1009 n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
1014 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1016 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1018 // epcEvent->ref == alpha
1019 // epcEvent->ref_to == sys
1020 // epcEvent->response == buffer num
1022 if ( mSharedBufferType != BUFFER_TYPE_SHM )
1027 int n = epcEvent->response;
1029 if( n >= 0 && n < SHARED_FILE_NUMBER )
1031 mCurrentSharedFile = n;
1033 delete mSharedFileInfo[n].mSharedFile;
1034 mSharedFileInfo[n].mSharedFile = NULL;
1036 delete mSharedFileInfo[n].mLock;
1037 mSharedFileInfo[n].mLock = NULL;
1039 std::stringstream sharedFileID;
1040 std::stringstream sharedFileNumber;
1042 sharedFileID << mSharedFileInfo[n].mSharedFileID;
1043 sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
1045 std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
1047 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
1049 mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
1050 if( mSharedFileInfo[n].mSharedFile != NULL )
1052 mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
1053 if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
1055 DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
1058 CreateNewImage( n );
1064 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1066 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1068 // epcEvent->ref == pixmap id
1069 // epcEvent->ref_to == type
1070 // epcEvent->response == buffer num
1072 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
1074 mSharedBufferType = (BufferType)(epcEvent->ref_to);
1076 ClearSharedFileInfo();
1078 mPixmap = static_cast<PixmapId>(epcEvent->ref);
1079 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "mPixmap [%x]", mPixmap);
1081 CreateNewPixmapImage();
1086 void Indicator::UpdateTopMargin()
1088 int newMargin = (mVisible == Dali::Window::VISIBLE && mOpacityMode == Dali::Window::OPAQUE) ? mImageHeight : 0;
1089 if (mTopMargin != newMargin)
1091 mTopMargin = newMargin;
1092 mAdaptor->IndicatorSizeChanged( mTopMargin );
1096 void Indicator::UpdateVisibility()
1098 if( CheckVisibleState() )
1100 // set default indicator type (enable the quick panel)
1101 OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
1105 // set default indicator type (disable the quick panel)
1106 OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
1111 mIndicatorContentActor.SetPosition( 0.0f, -mImageHeight, 0.0f );
1114 SetVisible(mVisible, true);
1117 void Indicator::UpdateImageData( int bufferNumber )
1119 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
1121 if( mState == CONNECTED && mVisible )
1125 // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
1126 CopyToBuffer( bufferNumber );
1132 mAdaptor->RequestUpdateOnce();
1138 bool Indicator::CopyToBuffer( int bufferNumber )
1140 bool success = false;
1142 if( mSharedFileInfo[bufferNumber].mLock )
1144 Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
1145 if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
1149 else if( scopedLock.IsLocked() )
1151 unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
1152 size_t size = static_cast< size_t >( mSharedFileInfo[bufferNumber].mImageWidth ) * mSharedFileInfo[bufferNumber].mImageHeight * 4;
1154 if( mIndicatorBuffer->UpdatePixels( src, size ) )
1156 mAdaptor->RequestUpdateOnce();
1165 void Indicator::CreateNewPixmapImage()
1167 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
1168 Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
1170 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
1171 SaveIndicatorImage( nativeImageSource );
1174 if( nativeImageSource )
1176 Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
1177 SetForegroundImage( texture );
1178 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
1179 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
1180 mEventActor.SetSize( mImageWidth, mImageHeight );
1185 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1187 if( mObserver != NULL )
1189 mObserver->IndicatorClosed( this );
1191 // Don't do connection in this callback - strange things happen!
1192 StartReconnectionTimer();
1196 void Indicator::CreateNewImage( int bufferNumber )
1198 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
1199 mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
1200 bool success = false;
1202 if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
1204 Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
1207 SetForegroundImage( texture );
1214 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1216 if( mObserver != NULL )
1218 mObserver->IndicatorClosed( this );
1220 // Don't do connection in this callback - strange things happen!
1221 StartReconnectionTimer();
1225 Dali::Geometry Indicator::CreateBackgroundGeometry()
1227 switch( mOpacityMode )
1229 case Dali::Window::TRANSLUCENT:
1230 if( !mTranslucentGeometry )
1232 // Construct 5 interval mesh
1246 struct BackgroundVertex
1252 unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1253 BackgroundVertex vertices[ numVertices ];
1256 float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1257 BackgroundVertex* currentVertex = vertices;
1258 for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1260 currentVertex->mPosition = Vector2( -0.5f, d );
1261 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1264 currentVertex->mPosition = Vector2( 0.5f, d );
1265 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1270 unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1271 unsigned short indices[ numIndices ];
1273 unsigned short* currentIndex = indices;
1274 for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1276 *currentIndex++ = (2 * y);
1277 *currentIndex++ = (2 * y) + 3;
1278 *currentIndex++ = (2 * y) + 1;
1280 *currentIndex++ = (2 * y);
1281 *currentIndex++ = (2 * y) + 2;
1282 *currentIndex++ = (2 * y) + 3;
1285 Dali::Property::Map vertexFormat;
1286 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1287 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1288 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1289 vertexPropertyBuffer.SetData( vertices, numVertices );
1291 // Create the geometry object
1292 mTranslucentGeometry = Dali::Geometry::New();
1293 mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1294 mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1297 return mTranslucentGeometry;
1298 case Dali::Window::OPAQUE:
1300 if( !mSolidGeometry )
1303 struct BackgroundVertex
1309 BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1310 { Vector2( -0.5f, 0.5f ), 1.0f }, { Vector2( 0.5f, 0.5f ), 1.0f } };
1313 unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1315 Dali::Property::Map vertexFormat;
1316 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1317 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1318 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1319 vertexPropertyBuffer.SetData( vertices, 4 );
1322 // Create the geometry object
1323 mSolidGeometry = Dali::Geometry::New();
1324 mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1325 mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1328 return mSolidGeometry;
1329 case Dali::Window::TRANSPARENT:
1333 return Dali::Geometry();
1336 void Indicator::SetForegroundImage( Dali::Texture texture )
1338 if( !mForegroundRenderer && texture )
1341 Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1343 // Create renderer from geometry and material
1344 Dali::Geometry quad = CreateQuadGeometry();
1345 mForegroundRenderer = Dali::Renderer::New( quad, shader );
1346 // Make sure the foreground stays in front of the background
1347 mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1349 // Set blend function
1350 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB, Dali::BlendFactor::ONE );
1351 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB, Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1352 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA, Dali::BlendFactor::ONE );
1353 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1355 // Create a texture-set and add to renderer
1357 Dali::TextureSet textureSet = Dali::TextureSet::New();
1358 textureSet.SetTexture( 0u, texture );
1359 mForegroundRenderer.SetTextures( textureSet );
1361 mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1363 else if( mForegroundRenderer )
1365 Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1366 textureSet.SetTexture( 0u, texture );
1369 if( mImageWidth == 0 && mImageHeight == 0 && texture)
1371 Resize( texture.GetWidth(), texture.GetHeight() );
1375 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1377 if( mObserver != NULL )
1379 mObserver->IndicatorTypeChanged( indicatorType );
1383 void Indicator::DataReceived( void* event )
1385 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1386 Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1388 switch( epcEvent->minor )
1392 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1395 mAdaptor->RequestUpdateOnce();
1399 case OP_UPDATE_DONE:
1401 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1402 // epcEvent->response == display buffer #
1403 UpdateImageData( epcEvent->response );
1408 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1409 SetSharedImageInfo( epcEvent );
1414 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1415 SetLockFileInfo( epcEvent );
1420 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1421 LoadSharedImage( epcEvent );
1426 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_PIXMAP_REF\n" );
1427 LoadPixmapImage( epcEvent );
1432 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1434 if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1436 IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1437 Resize( newSize->w, newSize->h );
1443 int msgDomain = epcEvent->ref;
1444 int msgId = epcEvent->ref_to;
1446 void *msgData = NULL;
1447 int msgDataSize = 0;
1448 msgData = epcEvent->data;
1449 msgDataSize = epcEvent->size;
1451 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1453 if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1457 case MSG_ID_INDICATOR_TYPE:
1459 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1460 Type* indicatorType = static_cast<Type*>( epcEvent->data );
1461 OnIndicatorTypeChanged( *indicatorType );
1465 case MSG_ID_INDICATOR_START_ANIMATION:
1467 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1469 if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1471 DALI_LOG_ERROR("Message data is incorrect\n");
1475 IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1477 if(!CheckVisibleState())
1479 ShowIndicator( animData->duration /* n sec */ );
1490 void Indicator::ConnectionClosed()
1492 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1494 // Will get this callback if the server connection failed to start up.
1495 delete mServerConnection;
1496 mServerConnection = NULL;
1497 mState = DISCONNECTED;
1499 // Attempt to re-connect
1503 bool Indicator::CheckVisibleState()
1505 if( mOrientation == Dali::Window::LANDSCAPE
1506 || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1507 || (mVisible == Dali::Window::INVISIBLE)
1508 || (mVisible == Dali::Window::AUTO && !mIsShowing) )
1516 void Indicator::ClearSharedFileInfo()
1518 for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1520 delete mSharedFileInfo[i].mLock;
1521 mSharedFileInfo[i].mLock = NULL;
1523 delete mSharedFileInfo[i].mSharedFile;
1524 mSharedFileInfo[i].mSharedFile = NULL;
1526 mSharedFileInfo[i].mLockFileName.clear();
1527 mSharedFileInfo[i].mSharedFileName.clear();
1532 * duration can be this
1536 * KEEP_SHOWING = -1,
1540 void Indicator::ShowIndicator(float duration)
1542 if( !mIndicatorAnimation )
1544 mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1545 mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1548 if(mIsShowing && !EqualsZero(duration))
1550 // If need to show during showing, do nothing.
1551 // In 2nd phase (below) will update timer
1553 else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1555 // If need to hide during hiding or hidden already, do nothing
1559 mIndicatorAnimation.Clear();
1561 if( EqualsZero(duration) )
1563 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1567 OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1571 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1575 OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1578 mIndicatorAnimation.Play();
1579 mIsAnimationPlaying = true;
1586 mShowTimer = Dali::Timer::New(1000 * duration);
1587 mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1589 mShowTimer.SetInterval(1000* duration);
1592 if( mVisible == Dali::Window::AUTO )
1594 // check the stage touch
1595 Dali::Stage::GetCurrent().TouchedSignal().Connect( this, &Indicator::OnStageTouched );
1600 if(mShowTimer && mShowTimer.IsRunning())
1605 if( mVisible == Dali::Window::AUTO )
1607 // check the stage touch
1608 Dali::Stage::GetCurrent().TouchedSignal().Disconnect( this, &Indicator::OnStageTouched );
1613 bool Indicator::OnShowTimer()
1615 // after time up, hide indicator
1616 ShowIndicator( HIDE_NOW );
1621 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1623 mIsAnimationPlaying = false;
1624 // once animation is finished and indicator is hidden, take it off stage
1625 if( mObserver != NULL )
1627 mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1631 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1633 // Nothing to do, but we still want to consume pan
1636 void Indicator::OnStageTouched(const Dali::TouchEvent& touchEvent)
1638 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
1640 // when stage is touched while indicator is showing temporary, hide it
1641 if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1643 switch( touchPoint.state )
1645 case Dali::PointState::DOWN:
1647 // if touch point is inside the indicator, indicator is not hidden
1648 if( mImageHeight < int(touchPoint.screen.y) )
1650 ShowIndicator( HIDE_NOW );