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