Merge "Move environment variable parsing to environment options" into tizen
[platform/core/uifw/dali-adaptor.git] / adaptors / common / indicator-impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "indicator-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <Ecore.h>
23 #include <Evas.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30
31 #include <dali/public-api/images/native-image.h>
32 #include <dali/public-api/events/touch-event.h>
33 #include <dali/public-api/events/touch-point.h>
34 #include <dali/public-api/common/stage.h>
35 #include <dali/public-api/actors/blending.h>
36 #include <dali/public-api/shader-effects/shader-effect.h>
37
38 #include <dali/integration-api/debug.h>
39
40 // INTERNAL INCLUDES
41 #include <adaptor-impl.h>
42 #include <accessibility-manager-impl.h>
43 #include <pixmap-image.h>
44
45 using Dali::Vector4;
46
47 #if defined(DEBUG_ENABLED)
48 #define STATE_DEBUG_STRING(state) (state==DISCONNECTED?"DISCONNECTED":state==CONNECTED?"CONNECTED":"UNKNOWN")
49 #endif
50
51 namespace
52 {
53
54 const float SLIDING_ANIMATION_DURATION( 0.2f ); // 200 milli seconds
55 const float AUTO_INDICATOR_STAY_DURATION(3.0f); // 3 seconds
56 const float SHOWING_DISTANCE_HEIGHT_RATE(0.34f); // 20 pixels
57
58 enum
59 {
60   KEEP_SHOWING = -1,
61   HIDE_NOW = 0
62 };
63
64 const int NUM_GRADIENT_INTERVALS(5); // Number of gradient intervals
65 const Dali::Vector4 GRADIENT_COLORS[NUM_GRADIENT_INTERVALS+1] =
66 {
67   Vector4(0.0f, 0.0f, 0.0f, 0.6f),
68   Vector4(0.0f, 0.0f, 0.0f, 0.38f),
69   Vector4(0.0f, 0.0f, 0.0f, 0.20f),
70   Vector4(0.0f, 0.0f, 0.0f, 0.08f),
71   Vector4(0.0f, 0.0f, 0.0f, 0.0f),
72   Vector4(0.0f, 0.0f, 0.0f, 0.0f),
73 };
74
75 const float OPAQUE_THRESHOLD(0.99f);
76 const float TRANSPARENT_THRESHOLD(0.05f);
77
78 // indicator service name
79 const char* INDICATOR_SERVICE_NAME("elm_indicator");
80
81 const char* MESH_VERTEX_SHADER =
82 "attribute lowp vec3     aColor;\n"
83 "varying   mediump vec4  vColor;\n"
84 "void main()\n"
85 "{\n"
86 "  gl_Position = uMvpMatrix * vec4(aPosition, 1.0);\n"
87 "  vColor = vec4(aColor.r, aColor.g, aColor.b, aTexCoord.x);\n"
88 "}\n";
89
90 const char* MESH_FRAGMENT_SHADER =
91 "varying mediump vec4  vColor;\n"
92 "void main()\n"
93 "{\n"
94 "  gl_FragColor = vColor*uColor;\n"
95 "}\n";
96
97 // Copied from ecore_evas_extn_engine.h
98
99 enum // opcodes
100 {
101    OP_RESIZE,
102    OP_SHOW,
103    OP_HIDE,
104    OP_FOCUS,
105    OP_UNFOCUS,
106    OP_UPDATE,
107    OP_UPDATE_DONE,
108    OP_SHM_REF0,
109    OP_SHM_REF1,
110    OP_SHM_REF2,
111    OP_PROFILE_CHANGE_REQUEST,
112    OP_PROFILE_CHANGE_DONE,
113    OP_EV_MOUSE_IN,
114    OP_EV_MOUSE_OUT,
115    OP_EV_MOUSE_UP,
116    OP_EV_MOUSE_DOWN,
117    OP_EV_MOUSE_MOVE,
118    OP_EV_MOUSE_WHEEL,
119    OP_EV_MULTI_UP,
120    OP_EV_MULTI_DOWN,
121    OP_EV_MULTI_MOVE,
122    OP_EV_KEY_UP,
123    OP_EV_KEY_DOWN,
124    OP_EV_HOLD,
125    OP_MSG_PARENT,
126    OP_MSG
127 };
128
129 // Copied from elm_conform.c
130
131 const int MSG_DOMAIN_CONTROL_INDICATOR( 0x10001 );
132 const int MSG_ID_INDICATOR_REPEAT_EVENT( 0x10002 );
133 const int MSG_ID_INDICATOR_ROTATION( 0x10003 );
134 const int MSG_ID_INDICATOR_OPACITY( 0X1004 );
135 const int MSG_ID_INDICATOR_TYPE( 0X1005 );
136 const int MSG_ID_INDICATOR_START_ANIMATION( 0X10006 );
137
138 struct IpcDataUpdate
139 {
140    int x, w, y, h;
141 };
142
143 struct IpcDataResize
144 {
145   int w, h;
146 };
147
148 struct IpcIndicatorDataAnimation
149 {
150   unsigned int xwin;
151   double       duration;
152 };
153
154 struct IpcDataEvMouseUp
155 {
156   int               b;
157   Evas_Button_Flags flags;
158   int               mask;
159   unsigned int      timestamp;
160   Evas_Event_Flags  event_flags;
161
162   IpcDataEvMouseUp(unsigned long timestamp)
163   : b(1),
164     flags(EVAS_BUTTON_NONE),
165     mask(0),
166     timestamp(static_cast<unsigned int>(timestamp)),
167     event_flags(EVAS_EVENT_FLAG_NONE)
168   {
169   }
170 };
171
172 struct IpcDataEvMouseDown
173 {
174   int                b;
175   Evas_Button_Flags  flags;
176   int                mask;
177   unsigned int       timestamp;
178   Evas_Event_Flags   event_flags;
179
180   IpcDataEvMouseDown(unsigned long timestamp)
181   : b(1),
182     flags(EVAS_BUTTON_NONE),
183     mask(0),
184     timestamp(static_cast<unsigned int>(timestamp)),
185     event_flags(EVAS_EVENT_FLAG_NONE)
186   {
187   }
188 };
189
190 struct IpcDataEvMouseMove
191 {
192   int                x, y;
193   Evas_Button_Flags  flags;
194   int                mask;
195   unsigned int       timestamp;
196   Evas_Event_Flags   event_flags;
197
198   IpcDataEvMouseMove(const Dali::TouchPoint& touchPoint, unsigned long timestamp)
199   : x(static_cast<Evas_Coord>(touchPoint.local.x)),
200     y(static_cast<Evas_Coord>(touchPoint.local.y)),
201     flags(EVAS_BUTTON_NONE),
202     mask(0),
203     timestamp(static_cast<unsigned int>(timestamp)),
204     event_flags(EVAS_EVENT_FLAG_NONE)
205   {
206   }
207 };
208
209 struct IpcDataEvMouseOut
210 {
211   unsigned int     timestamp;
212   int              mask;
213   Evas_Event_Flags event_flags;
214
215   IpcDataEvMouseOut(unsigned long timestamp)
216   : timestamp(static_cast<unsigned int>(timestamp)),
217     mask(0),
218     event_flags(EVAS_EVENT_FLAG_NONE)
219   {
220   }
221 };
222
223 void SetMeshDataColors(Dali::AnimatableMesh mesh, const Vector4 (&colors)[NUM_GRADIENT_INTERVALS+1])
224 {
225   for( size_t i=0; i<NUM_GRADIENT_INTERVALS+1; i++ )
226   {
227     int j=i*2;
228     mesh[j].SetColor(colors[i]);
229     mesh[j+1].SetColor(colors[i]);
230     mesh[j].SetTextureCoords(Dali::Vector2(colors[i].a, colors[i].a));
231     mesh[j+1].SetTextureCoords(Dali::Vector2(colors[i].a, colors[i].a));
232   }
233 }
234
235 void SetMeshDataColors(Dali::AnimatableMesh mesh, const Vector4& color)
236 {
237   for( size_t i=0, length=NUM_GRADIENT_INTERVALS+1 ; i<length; i++ )
238   {
239     int j=i*2;
240     mesh[j].SetColor(color);
241     mesh[j+1].SetColor(color);
242     mesh[j].SetTextureCoords(Dali::Vector2(color.a, color.a));
243     mesh[j+1].SetTextureCoords(Dali::Vector2(color.a, color.a));
244   }
245 }
246
247 } // anonymous namespace
248
249
250 namespace Dali
251 {
252 namespace Internal
253 {
254 namespace Adaptor
255 {
256 #if defined(DEBUG_ENABLED)
257 Debug::Filter* gIndicatorLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_INDICATOR");
258 #endif
259
260
261 Indicator::LockFile::LockFile(const std::string filename)
262 : mFilename(filename),
263   mErrorThrown(false)
264 {
265   mFileDescriptor = open(filename.c_str(), O_RDWR);
266   if( mFileDescriptor == -1 )
267   {
268     mFileDescriptor = 0;
269     mErrorThrown = true;
270     DALI_LOG_ERROR( "### Cannot open %s for indicator lock ###\n", mFilename.c_str() );
271   }
272 }
273
274 Indicator::LockFile::~LockFile()
275 {
276   // Closing file descriptor also unlocks file.
277   close( mFileDescriptor );
278 }
279
280 bool Indicator::LockFile::Lock()
281 {
282   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
283
284   bool locked = false;
285   if( mFileDescriptor > 0 )
286   {
287     if( lockf( mFileDescriptor, F_LOCK, 0 ) == 0 ) // Note, operation may block.
288     {
289       locked = true;
290     }
291     else
292     {
293       if( errno == EBADF )
294       {
295         // file descriptor is no longer valid or not writable
296         mFileDescriptor = 0;
297         mErrorThrown = true;
298         DALI_LOG_ERROR( "### Cannot lock indicator: bad file descriptor for %s ###\n", mFilename.c_str() );
299       }
300     }
301   }
302
303   return locked;
304 }
305
306 void Indicator::LockFile::Unlock()
307 {
308   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
309   if( lockf( mFileDescriptor, F_ULOCK, 0 ) != 0 )
310   {
311     if( errno == EBADF )
312     {
313       // file descriptor is no longer valid or not writable
314       mFileDescriptor = 0;
315       mErrorThrown = true;
316       DALI_LOG_ERROR( "### Cannot unlock indicator: bad file descriptor for %s\n", mFilename.c_str() );
317     }
318   }
319 }
320
321 bool Indicator::LockFile::RetrieveAndClearErrorStatus()
322 {
323   bool error = mErrorThrown;
324   mErrorThrown = false;
325   return error;
326 }
327
328 Indicator::ScopedLock::ScopedLock(LockFile* lockFile)
329 : mLockFile(lockFile),
330   mLocked(false)
331 {
332   if(mLockFile)
333   {
334     mLocked = mLockFile->Lock();
335   }
336 }
337
338 Indicator::ScopedLock::~ScopedLock()
339 {
340   if( mLockFile )
341   {
342     mLockFile->Unlock();
343   }
344 }
345
346 bool Indicator::ScopedLock::IsLocked()
347 {
348   return mLocked;
349 }
350
351 Indicator::Indicator( Adaptor* adaptor, Dali::Window::WindowOrientation orientation, Observer* observer )
352 : mPixmap( 0 ),
353   mGestureDetected( false ),
354   mConnection( this ),
355   mOpacityMode( Dali::Window::OPAQUE ),
356   mState( DISCONNECTED ),
357   mAdaptor(adaptor),
358   mServerConnection( NULL ),
359   mObserver( observer ),
360   mOrientation( orientation ),
361   mImageWidth( 0 ),
362   mImageHeight( 0 ),
363   mVisible( Dali::Window::INVISIBLE ),
364   mIsShowing( true ),
365   mIsAnimationPlaying( false ),
366   mCurrentSharedFile( 0 )
367 {
368   mIndicatorImageActor = Dali::ImageActor::New();
369   mIndicatorImageActor.SetBlendFunc( Dali::BlendingFactor::ONE, Dali::BlendingFactor::ONE_MINUS_SRC_ALPHA,
370                                     Dali::BlendingFactor::ONE, Dali::BlendingFactor::ONE );
371
372   mIndicatorImageActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
373   mIndicatorImageActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
374
375   // Indicator image handles the touch event including "leave"
376   mIndicatorImageActor.SetLeaveRequired( true );
377   mIndicatorImageActor.TouchedSignal().Connect( this, &Indicator::OnTouched );
378
379   SetBackground();
380   mBackgroundActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
381   mBackgroundActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
382   mBackgroundActor.SetZ( -0.02f );
383
384   // add background to image actor to move it with indicator image
385   mIndicatorImageActor.Add( mBackgroundActor );
386
387   mIndicatorActor = Dali::Actor::New();
388   mIndicatorActor.Add( mIndicatorImageActor );
389
390   if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
391   {
392     mBackgroundActor.SetVisible( false );
393   }
394
395   // Event handler to find out flick down gesture
396   mEventActor = Dali::Actor::New();
397   mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
398   mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
399   mEventActor.SetZ( -0.01f );
400   mIndicatorActor.Add( mEventActor );
401
402   // Attach pan gesture to find flick down during hiding.
403   // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
404   // since it consumes the pan gesture in advance.
405   mPanDetector = Dali::PanGestureDetector::New();
406   mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
407   mPanDetector.Attach( mEventActor );
408
409   Open( orientation );
410
411   // register indicator to accessibility manager
412   Dali::AccessibilityManager accessibilityManager = AccessibilityManager::Get();
413   if(accessibilityManager)
414   {
415     AccessibilityManager::GetImplementation( accessibilityManager ).SetIndicator( this );
416   }
417   // hide the indicator by default
418   mIndicatorActor.SetVisible( false );
419 }
420
421 Indicator::~Indicator()
422 {
423   if(mEventActor)
424   {
425     mEventActor.TouchedSignal().Disconnect( this, &Indicator::OnTouched );
426   }
427   Disconnect();
428 }
429
430 void Indicator::SetAdaptor(Adaptor* adaptor)
431 {
432   mAdaptor = adaptor;
433   mIndicatorBuffer->SetAdaptor( adaptor );
434 }
435
436 Dali::Actor Indicator::GetActor()
437 {
438   return mIndicatorActor;
439 }
440
441 void Indicator::Open( Dali::Window::WindowOrientation orientation )
442 {
443   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
444
445   // Calls from Window should be set up to ensure we are in a
446   // disconnected state before opening a second time.
447   DALI_ASSERT_DEBUG( mState == DISCONNECTED );
448
449   mOrientation = orientation;
450
451   Connect();
452
453   // Change background visibility depending on orientation
454   if(mOrientation == Dali::Window::PORTRAIT || mOrientation == Dali::Window::PORTRAIT_INVERSE)
455   {
456     mBackgroundActor.SetVisible(true);
457   }
458   else
459   {
460     mBackgroundActor.SetVisible(false);
461   }
462 }
463
464 void Indicator::Close()
465 {
466   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
467
468   if( mState == CONNECTED )
469   {
470     Disconnect();
471     if( mObserver != NULL )
472     {
473       mObserver->IndicatorClosed( this );
474     }
475   }
476
477   Dali::Image emptyImage;
478   mIndicatorImageActor.SetImage(emptyImage);
479 }
480
481 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
482 {
483   mOpacityMode = mode;
484   SetBackground();
485 }
486
487 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
488 {
489   if ( visibleMode != mVisible || forceUpdate )
490   {
491     // If we were previously hidden, then we should update the image data before we display the indicator
492     if ( mVisible == Dali::Window::INVISIBLE )
493     {
494       UpdateImageData( mCurrentSharedFile );
495     }
496     if ( visibleMode != Dali::Window::INVISIBLE )
497     {
498       mIndicatorActor.SetVisible( true );
499     }
500
501     mVisible = visibleMode;
502
503     if( mIndicatorImageActor.GetImage() )
504     {
505       if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
506       {
507         // hide indicator
508         ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
509       }
510       else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
511       {
512         // show indicator
513         ShowIndicator( KEEP_SHOWING );
514       }
515       else
516       {
517         // hide indicator
518         ShowIndicator( HIDE_NOW );
519       }
520     }
521   }
522 }
523
524 bool Indicator::IsConnected()
525 {
526   return ( mState == CONNECTED );
527 }
528
529 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
530 {
531  if(IsConnected())
532  {
533    return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
534  }
535  else
536  {
537    return false;
538  }
539 }
540
541 bool Indicator::OnTouched(Dali::Actor indicator, const Dali::TouchEvent& touchEvent)
542 {
543   if( mServerConnection )
544   {
545     const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
546
547     // Send touch event to indicator server when indicator is showing
548     if( CheckVisibleState() || mIsShowing )
549     {
550       switch( touchPoint.state )
551       {
552         case Dali::TouchPoint::Down:
553         {
554           IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
555           IpcDataEvMouseDown ipcDown( touchEvent.time );
556           mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
557           mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
558
559           if( mVisible == Dali::Window::AUTO )
560           {
561             // Stop hiding indicator
562             ShowIndicator( KEEP_SHOWING );
563           }
564         }
565         break;
566
567         case Dali::TouchPoint::Motion:
568         {
569           IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
570           mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
571         }
572         break;
573
574         case Dali::TouchPoint::Up:
575         {
576           IpcDataEvMouseUp ipcUp( touchEvent.time );
577           mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
578
579           if( mVisible == Dali::Window::AUTO )
580           {
581             // Hide indicator
582             ShowIndicator( 0.5f /* hide after 0.5 sec */ );
583           }
584         }
585         break;
586
587         case Dali::TouchPoint::Leave:
588         {
589           IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
590           mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
591           IpcDataEvMouseUp ipcOut( touchEvent.time );
592           mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
593         }
594         break;
595
596         default:
597           break;
598       }
599
600       return true;
601     }
602   }
603
604   return false;
605 }
606
607 bool Indicator::Connect()
608 {
609   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
610
611   DALI_ASSERT_DEBUG( mState == DISCONNECTED );
612
613   bool connected = false;
614
615   mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
616   if( mServerConnection )
617   {
618     connected = mServerConnection->IsConnected();
619     if( ! connected )
620     {
621       delete mServerConnection;
622       mServerConnection = NULL;
623     }
624   }
625
626   if( !connected )
627   {
628     StartReconnectionTimer();
629   }
630   else
631   {
632     mState = CONNECTED;
633   }
634
635   return connected;
636 }
637
638 void Indicator::StartReconnectionTimer()
639 {
640   if( ! mReconnectTimer )
641   {
642     mReconnectTimer = Dali::Timer::New(1000);
643     mConnection.DisconnectAll();
644     mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
645   }
646   mReconnectTimer.Start();
647 }
648
649 bool Indicator::OnReconnectTimer()
650 {
651   bool retry = false;
652
653   if( mState == DISCONNECTED )
654   {
655     if( !Connect() )
656     {
657       retry = true;
658     }
659   }
660
661   return retry;
662 }
663
664 void Indicator::Disconnect()
665 {
666   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
667
668   mState = DISCONNECTED;
669
670   delete mServerConnection;
671   mServerConnection = NULL;
672
673   ClearSharedFileInfo();
674 }
675
676 void Indicator::Resize( int width, int height )
677 {
678   if( width < 1 )
679   {
680     width = 1;
681   }
682   if( height < 1 )
683   {
684     height = 1;
685   }
686
687   if( mImageWidth != width || mImageHeight != height )
688   {
689     mImageWidth = width;
690     mImageHeight = height;
691
692     mIndicatorImageActor.SetSize( mImageWidth, mImageHeight );
693     mIndicatorActor.SetSize( mImageWidth, mImageHeight );
694     mEventActor.SetSize(mImageWidth, mImageHeight);
695
696     SetBackground();
697     if( mBackgroundActor )
698     {
699       mBackgroundActor.SetSize( mImageWidth, mImageHeight );
700     }
701   }
702 }
703
704 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
705 {
706   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
707
708   // epcEvent->ref == w
709   // epcEvent->ref_to == h
710   // epcEvent->response == buffer num
711   // epcEvent->data = lockfile + nul byte
712
713   if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
714       (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
715   {
716     int n = epcEvent->response;
717
718     if( n >= 0 && n < SHARED_FILE_NUMBER )
719     {
720       mCurrentSharedFile = n;
721
722       mSharedFileInfo[n].mImageWidth  = epcEvent->ref;
723       mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
724
725       mSharedFileInfo[n].mLockFileName.clear();
726
727       mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
728
729       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
730                      n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
731     }
732   }
733 }
734
735 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
736 {
737   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
738
739   // epcEvent->ref == shm id
740   // epcEvent->ref_to == shm num
741   // epcEvent->response == buffer num
742   // epcEvent->data = shm ref string + nul byte
743
744   if ( (epcEvent->data) &&
745        (epcEvent->size > 0) &&
746        (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
747   {
748     int n = epcEvent->response;
749
750     if( n >= 0 && n < SHARED_FILE_NUMBER )
751     {
752       mCurrentSharedFile = n;
753
754       mSharedFileInfo[n].mSharedFileName.clear();
755
756       mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
757
758       mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
759       mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
760
761       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
762                      n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
763     }
764   }
765 }
766
767 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
768 {
769   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
770
771   // epcEvent->ref == alpha
772   // epcEvent->ref_to == sys
773   // epcEvent->response == buffer num
774
775   int n = epcEvent->response;
776
777   if( n >= 0 && n < SHARED_FILE_NUMBER )
778   {
779     mCurrentSharedFile = n;
780
781     delete mSharedFileInfo[n].mSharedFile;
782     mSharedFileInfo[n].mSharedFile = NULL;
783
784     delete mSharedFileInfo[n].mLock;
785     mSharedFileInfo[n].mLock = NULL;
786
787     std::stringstream sharedFileID;
788     std::stringstream sharedFileNumber;
789
790     sharedFileID << mSharedFileInfo[n].mSharedFileID;
791     sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
792
793     std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
794
795     DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
796
797     mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
798     if( mSharedFileInfo[n].mSharedFile != NULL )
799     {
800       mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
801       if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
802       {
803         DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
804       }
805
806       CreateNewImage( n );
807
808       if( CheckVisibleState() )
809       {
810         // set default indicator type (enable the quick panel)
811         OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
812       }
813       else
814       {
815         // set default indicator type (disable the quick panel)
816         OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
817       }
818
819       SetVisible(mVisible, true);
820     }
821   }
822 }
823
824 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
825 {
826   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
827
828   // epcEvent->ref == w
829   // epcEvent->ref_to == h
830   // epcEvent->response == alpha
831   // epcEvent->data = pixmap id
832
833   if( ( epcEvent->data ) &&
834       (epcEvent->size >= (int)sizeof(PixmapId)) )
835   {
836     ClearSharedFileInfo();
837
838     if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
839     {
840       mImageWidth  = epcEvent->ref;
841       mImageHeight = epcEvent->ref_to;
842
843       mPixmap = *(static_cast<PixmapId*>(epcEvent->data));
844       CreateNewPixmapImage();
845
846       if( CheckVisibleState() )
847       {
848         // set default indicator type (enable the quick panel)
849         OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
850       }
851       else
852       {
853         // set default indicator type (disable the quick panel)
854         OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
855       }
856
857       SetVisible(mVisible, true);
858     }
859   }
860 }
861
862 void Indicator::UpdateImageData( int bufferNumber )
863 {
864   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s  mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
865
866   if( mState == CONNECTED && mVisible )
867   {
868     if(mPixmap == 0)
869     {
870       // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
871       CopyToBuffer( bufferNumber );
872     }
873     else
874     {
875       if(mIsShowing)
876       {
877         mAdaptor->RequestUpdateOnce();
878       }
879     }
880   }
881 }
882
883 bool Indicator::CopyToBuffer( int bufferNumber )
884 {
885   bool success = false;
886
887   if( mSharedFileInfo[bufferNumber].mLock )
888   {
889     Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
890     if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
891     {
892       // Do nothing here.
893     }
894     else if( scopedLock.IsLocked() )
895     {
896       unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
897       size_t size = mSharedFileInfo[bufferNumber].mImageWidth * mSharedFileInfo[bufferNumber].mImageHeight * 4;
898
899       if( mIndicatorBuffer->UpdatePixels( src, size ) )
900       {
901         mAdaptor->RequestUpdateOnce();
902         success = true;
903       }
904     }
905   }
906
907   return success;
908 }
909
910 void Indicator::SetBackground()
911 {
912   if( ! mBackgroundActor )
913   {
914     ConstructBackgroundMesh();
915   }
916
917   switch( mOpacityMode )
918   {
919     case Dali::Window::TRANSLUCENT:
920     {
921       SetMeshDataColors( mBackgroundMesh, GRADIENT_COLORS );
922     }
923     break;
924
925     case Dali::Window::TRANSPARENT:
926     {
927       SetMeshDataColors( mBackgroundMesh, Color::TRANSPARENT );
928     }
929     break;
930
931     case Dali::Window::OPAQUE:
932     default :
933     {
934       SetMeshDataColors( mBackgroundMesh, Color::BLACK );
935     }
936     break;
937   }
938 }
939
940 void Indicator::CreateNewPixmapImage()
941 {
942   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
943   Dali::PixmapImagePtr pixmapImage = Dali::PixmapImage::New( mPixmap );
944
945   if( pixmapImage )
946   {
947     mIndicatorImageActor.SetImage( Dali::NativeImage::New(*pixmapImage) );
948     mIndicatorImageActor.SetSize( mImageWidth, mImageHeight );
949     mIndicatorActor.SetSize( mImageWidth, mImageHeight );
950     mEventActor.SetSize(mImageWidth, mImageHeight);
951
952     SetBackground();
953     if( mBackgroundActor )
954     {
955       mBackgroundActor.SetSize( mImageWidth, mImageHeight );
956     }
957   }
958   else
959   {
960     DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
961     Disconnect();
962     if( mObserver != NULL )
963     {
964       mObserver->IndicatorClosed( this );
965     }
966     // Don't do connection in this callback - strange things happen!
967     StartReconnectionTimer();
968   }
969 }
970
971 void Indicator::CreateNewImage( int bufferNumber )
972 {
973   DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
974   mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
975   Dali::Image image = Dali::NativeImage::New( mIndicatorBuffer->GetNativeImage() );
976
977   if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
978   {
979     mIndicatorImageActor.SetImage( image );
980   }
981   else
982   {
983     DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
984     Disconnect();
985     if( mObserver != NULL )
986     {
987       mObserver->IndicatorClosed( this );
988     }
989     // Don't do connection in this callback - strange things happen!
990     StartReconnectionTimer();
991   }
992 }
993
994 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
995 {
996   if( mObserver != NULL )
997   {
998     mObserver->IndicatorTypeChanged( indicatorType );
999   }
1000 }
1001
1002 void Indicator::DataReceived( void* event )
1003 {
1004   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1005   Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1006
1007   switch( epcEvent->minor )
1008   {
1009     case OP_UPDATE:
1010     {
1011       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1012       if( mPixmap != 0 && mIsShowing )
1013       {
1014         mAdaptor->RequestUpdateOnce();
1015       }
1016       break;
1017     }
1018     case OP_UPDATE_DONE:
1019     {
1020       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1021       // epcEvent->response == display buffer #
1022       UpdateImageData( epcEvent->response );
1023       break;
1024     }
1025     case OP_SHM_REF0:
1026     {
1027       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1028       SetSharedImageInfo( epcEvent );
1029       break;
1030     }
1031     case OP_SHM_REF1:
1032     {
1033       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1034       SetLockFileInfo( epcEvent );
1035       break;
1036     }
1037     case OP_SHM_REF2:
1038     {
1039       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1040       LoadSharedImage( epcEvent );
1041       break;
1042     }
1043     case OP_RESIZE:
1044     {
1045       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1046
1047       if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1048       {
1049         IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1050         Resize( newSize->w, newSize->h );
1051       }
1052       break;
1053     }
1054     case OP_MSG_PARENT:
1055     {
1056       int msgDomain = epcEvent->ref;
1057       int msgId = epcEvent->ref_to;
1058
1059       void *msgData = NULL;
1060       int msgDataSize = 0;
1061       msgData = epcEvent->data;
1062       msgDataSize = epcEvent->size;
1063
1064       DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1065
1066       if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1067       {
1068         switch( msgId )
1069         {
1070           case MSG_ID_INDICATOR_TYPE:
1071           {
1072             DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1073             Type* indicatorType = static_cast<Type*>( epcEvent->data );
1074             OnIndicatorTypeChanged( *indicatorType );
1075             break;
1076           }
1077
1078           case MSG_ID_INDICATOR_START_ANIMATION:
1079           {
1080             DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1081
1082             if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1083             {
1084               DALI_LOG_ERROR("Message data is incorrect");
1085               break;
1086             }
1087
1088             IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1089
1090             if(!CheckVisibleState())
1091             {
1092               ShowIndicator( animData->duration /* n sec */ );
1093             }
1094             break;
1095           }
1096
1097         }
1098       }
1099       break;
1100     }
1101   }
1102 }
1103
1104 void Indicator::ConnectionClosed()
1105 {
1106   DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1107
1108   // Will get this callback if the server connection failed to start up.
1109   delete mServerConnection;
1110   mServerConnection = NULL;
1111   mState = DISCONNECTED;
1112
1113   // Attempt to re-connect
1114   Connect();
1115 }
1116
1117 bool Indicator::CheckVisibleState()
1118 {
1119   if( mOrientation == Dali::Window::LANDSCAPE
1120     || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1121     || (mVisible != Dali::Window::VISIBLE) )
1122   {
1123     return false;
1124   }
1125
1126   return true;
1127 }
1128
1129 void Indicator::ConstructBackgroundMesh()
1130 {
1131   // Construct 5 interval mesh
1132   // 0  +---+  1
1133   //    | \ |
1134   // 2  +---+  3
1135   //    | \ |
1136   // 4  +---+  5
1137   //    | \ |
1138   // 6  +---+  7
1139   //    | \ |
1140   // 8  +---+  9
1141   //    | \ |
1142   // 10 +---+  11
1143   Dali::AnimatableMesh::Faces faces;
1144   faces.reserve(NUM_GRADIENT_INTERVALS * 6); // 2 tris per interval
1145   for(int i=0; i<NUM_GRADIENT_INTERVALS; i++)
1146   {
1147     int j=i*2;
1148     faces.push_back(j); faces.push_back(j+3); faces.push_back(j+1);
1149     faces.push_back(j); faces.push_back(j+2); faces.push_back(j+3);
1150   }
1151
1152   mBackgroundMesh = Dali::AnimatableMesh::New((NUM_GRADIENT_INTERVALS+1)*2, faces);
1153   float interval=1.0f / (float)NUM_GRADIENT_INTERVALS;
1154   for(int i=0;i<NUM_GRADIENT_INTERVALS+1;i++)
1155   {
1156     int j=i*2;
1157     mBackgroundMesh[j  ].SetPosition(Vector3(-0.5f, -0.5f+(interval*(float)i), 0.0f));
1158     mBackgroundMesh[j+1].SetPosition(Vector3( 0.5f, -0.5f+(interval*(float)i), 0.0f));
1159   }
1160
1161   mBackgroundActor = Dali::MeshActor::New(mBackgroundMesh);
1162   Dali::ShaderEffect shaderEffect = Dali::ShaderEffect::New( MESH_VERTEX_SHADER, MESH_FRAGMENT_SHADER,
1163                                                              GEOMETRY_TYPE_UNTEXTURED_MESH, // Using vertex color
1164                                                              Dali::ShaderEffect::HINT_BLENDING );
1165   mBackgroundActor.SetShaderEffect(shaderEffect);
1166 }
1167
1168 void Indicator::ClearSharedFileInfo()
1169 {
1170   for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1171   {
1172     delete mSharedFileInfo[i].mLock;
1173     mSharedFileInfo[i].mLock = NULL;
1174
1175     delete mSharedFileInfo[i].mSharedFile;
1176     mSharedFileInfo[i].mSharedFile = NULL;
1177
1178     mSharedFileInfo[i].mLockFileName.clear();
1179     mSharedFileInfo[i].mSharedFileName.clear();
1180   }
1181 }
1182
1183 /**
1184  * duration can be this
1185  *
1186  * enum
1187  * {
1188  *  KEEP_SHOWING = -1,
1189  *  HIDE_NOW = 0
1190  * };
1191  */
1192 void Indicator::ShowIndicator(float duration)
1193 {
1194   if( !mIndicatorAnimation )
1195   {
1196     mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1197     mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1198   }
1199
1200   if(mIsShowing && !EqualsZero(duration))
1201   {
1202     // If need to show during showing, do nothing.
1203     // In 2nd phase (below) will update timer
1204   }
1205   else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1206   {
1207     // If need to hide during hiding or hidden already, do nothing
1208   }
1209   else
1210   {
1211     if( EqualsZero(duration) )
1212     {
1213       mIndicatorAnimation.AnimateTo( Property( mIndicatorImageActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunctions::EaseOut );
1214
1215       mIsShowing = false;
1216
1217       OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1218     }
1219     else
1220     {
1221       mIndicatorAnimation.AnimateTo( Property( mIndicatorImageActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunctions::EaseOut );
1222
1223       mIsShowing = true;
1224
1225       OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1226     }
1227
1228     mIndicatorAnimation.Play();
1229     mIsAnimationPlaying = true;
1230   }
1231
1232   if(duration > 0)
1233   {
1234     if(!mShowTimer)
1235     {
1236       mShowTimer = Dali::Timer::New(1000 * duration);
1237       mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1238     }
1239     mShowTimer.SetInterval(1000* duration);
1240     mShowTimer.Start();
1241
1242     if( mVisible == Dali::Window::AUTO )
1243     {
1244       // check the stage touch
1245       Dali::Stage::GetCurrent().TouchedSignal().Connect( this, &Indicator::OnStageTouched );
1246     }
1247   }
1248   else
1249   {
1250     if(mShowTimer && mShowTimer.IsRunning())
1251     {
1252       mShowTimer.Stop();
1253     }
1254
1255     if( mVisible == Dali::Window::AUTO )
1256     {
1257       // check the stage touch
1258       Dali::Stage::GetCurrent().TouchedSignal().Disconnect( this, &Indicator::OnStageTouched );
1259     }
1260   }
1261 }
1262
1263 bool Indicator::OnShowTimer()
1264 {
1265   // after time up, hide indicator
1266   ShowIndicator( HIDE_NOW );
1267
1268   return false;
1269 }
1270
1271 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1272 {
1273   mIsAnimationPlaying = false;
1274   // once animation is finished and indicator is hidden, take it off stage
1275   if( !mIsShowing )
1276   {
1277     mIndicatorActor.SetVisible( false );
1278
1279     if( mObserver != NULL )
1280     {
1281       mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1282     }
1283   }
1284 }
1285
1286 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1287 {
1288   if( mServerConnection )
1289   {
1290     switch( gesture.state )
1291     {
1292       case Gesture::Started:
1293       {
1294         mGestureDetected = false;
1295
1296         // The gesture position is the current position after it has moved by the displacement.
1297         // We want to reference the original position.
1298         mGestureDeltaY = gesture.position.y - gesture.displacement.y;
1299       }
1300
1301       // No break, Fall through
1302       case Gesture::Continuing:
1303       {
1304         if( mVisible == Dali::Window::AUTO && !mIsShowing )
1305         {
1306           // Only take one touch point
1307           if( gesture.numberOfTouches == 1 && mGestureDetected == false )
1308           {
1309             mGestureDeltaY += gesture.displacement.y;
1310
1311             if( mGestureDeltaY >= mImageHeight * SHOWING_DISTANCE_HEIGHT_RATE )
1312             {
1313               ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1314               mGestureDetected = true;
1315             }
1316           }
1317         }
1318
1319         break;
1320       }
1321
1322       case Gesture::Finished:
1323       case Gesture::Cancelled:
1324       {
1325         // if indicator is showing, hide again when touching is finished (Since touch leave is activated, checking it in gesture::finish instead of touch::up)
1326         if( mVisible == Dali::Window::AUTO && mIsShowing )
1327         {
1328           ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1329         }
1330         break;
1331       }
1332
1333
1334       default:
1335         break;
1336     }
1337   }
1338 }
1339
1340 void Indicator::OnStageTouched(const Dali::TouchEvent& touchEvent)
1341 {
1342   const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
1343
1344   // when stage is touched while indicator is showing temporary, hide it
1345   if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1346   {
1347     switch( touchPoint.state )
1348     {
1349       case Dali::TouchPoint::Down:
1350       {
1351         ShowIndicator( HIDE_NOW );
1352         break;
1353       }
1354
1355       default:
1356       break;
1357     }
1358   }
1359 }
1360
1361 } // Adaptor
1362 } // Internal
1363 } // Dali