Support for the macOS platform
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / macos / timer-impl-mac.cpp
1 /*
2  * Copyright (c) 2020 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 <dali/internal/system/common/timer-impl.h>
20 #include "extern-definitions.h"
21
22 namespace Dali::Internal::Adaptor
23 {
24
25
26 /**
27  * Struct to hide away macOS implementation details
28  */
29 struct Timer::Impl
30 {
31   Impl(Timer *parent, unsigned int milliSec)
32     : mTimer(CreateTimer(parent, milliSec)) {}
33
34   ~Impl() { Stop(); }
35
36   static void TimerProc(CFRunLoopTimerRef timer, void *info);
37
38   void Start();
39   void Stop();
40   void Reset(Timer *parent, unsigned int milliSec);
41
42   unsigned int GetInterval() const noexcept
43   {
44     return CFRunLoopTimerGetInterval(mTimer.get()) * 1000.0;
45   }
46
47   bool IsRunning() const noexcept
48   {
49     return CFRunLoopTimerIsValid(mTimer.get());
50   }
51
52 private:
53   CFRef<CFRunLoopTimerRef> CreateTimer(Timer *parent, unsigned int milliSec);
54
55   CFRef<CFRunLoopTimerRef> mTimer;
56 };
57
58 void Timer::Impl::TimerProc(CFRunLoopTimerRef timer, void *info)
59 {
60   auto *pTimer = static_cast<Timer*>(info);
61   pTimer->Tick();
62 }
63
64 void Timer::Impl::Start()
65 {
66   if (!IsRunning())
67   {
68     auto runLoop = CFRunLoopGetMain();
69     CFRunLoopAddTimer(runLoop, mTimer.get(), kCFRunLoopDefaultMode);
70   }
71 }
72
73 void Timer::Impl::Stop()
74 {
75   if (IsRunning())
76   {
77     CFRunLoopTimerContext context;
78     CFRunLoopTimerGetContext(mTimer.get(), &context);
79     const auto interval = CFRunLoopTimerGetInterval(mTimer.get());
80     CFRunLoopTimerInvalidate(mTimer.get());
81
82     // After we invalidate the timer, we can't reuse it, so we create
83     // a new timer for case the user calls Start again
84     const auto fireDate = CFAbsoluteTimeGetCurrent() + interval;
85     mTimer.reset(CFRunLoopTimerCreate(
86       kCFAllocatorDefault,
87       fireDate,
88       interval,
89       0,
90       0,
91       TimerProc,
92       &context
93     ));
94   }
95 }
96
97 void Timer::Impl::Reset(Timer *parent, unsigned int milliSec)
98 {
99   Stop();
100   mTimer = CreateTimer(parent, milliSec);
101   Start();
102 }
103
104 CFRef<CFRunLoopTimerRef>
105 Timer::Impl::CreateTimer(Timer *parent, unsigned int milliSec)
106 {
107   const auto interval = static_cast<CFAbsoluteTime>(milliSec) / 1000;
108   const auto fireDate = CFAbsoluteTimeGetCurrent() + interval;
109   CFRunLoopTimerContext context =
110   {
111     .version = 0,
112     .info = parent,
113     .retain = nullptr,
114     .release = nullptr,
115   };
116
117   return MakeRef(CFRunLoopTimerCreate(
118     kCFAllocatorDefault,
119     fireDate, interval,
120     0,
121     0,
122     TimerProc,
123     &context
124   ));
125 }
126
127 TimerPtr Timer::New( unsigned int milliSec )
128 {
129   return new Timer( milliSec );
130 }
131
132 Timer::Timer( unsigned int milliSec )
133 : mImpl(new Impl(this, milliSec))
134 {
135 }
136
137 Timer::~Timer()
138 {
139   // stop timers
140   Stop();
141
142   delete mImpl;
143   mImpl = NULL;
144 }
145
146 void Timer::Start()
147 {
148   mImpl->Start();
149 }
150
151 void Timer::Stop()
152 {
153   mImpl->Stop();
154 }
155
156 void Timer::Pause()
157 {
158
159 }
160
161 void Timer::Resume()
162 {
163
164 }
165
166 void Timer::SetInterval( unsigned int interval, bool restart )
167 {
168   mImpl->Reset(this, interval);
169 }
170
171 unsigned int Timer::GetInterval() const
172 {
173   return mImpl->GetInterval();
174 }
175
176 bool Timer::Tick()
177 {
178   // Guard against destruction during signal emission
179   Dali::Timer handle( this );
180
181   bool retVal( false );
182
183   // Override with new signal if used
184   if( !mTickSignal.Empty() )
185   {
186     retVal = mTickSignal.Emit();
187
188     // Timer stops if return value is false
189     if (retVal == false)
190     {
191       Stop();
192     }
193     else
194     {
195       retVal = true;   // continue emission
196     }
197   }
198   else // no callbacks registered
199   {
200     // periodic timer is started but nobody listens, continue
201     retVal = true;
202   }
203
204   return retVal;
205 }
206
207 Dali::Timer::TimerSignalType& Timer::TickSignal()
208 {
209   return mTickSignal;
210 }
211
212 bool Timer::IsRunning() const
213 {
214   return mImpl->IsRunning();
215 }
216
217 } // namespace Dali::Internal::Adaptor
218