[NUI] Change all CallingConvention to `Cdecl`
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / Utility / Timer.cs
1 /*
2  * Copyright(c) 2021 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 using System;
18 using System.Runtime.InteropServices;
19 using System.ComponentModel;
20 using System.Threading;
21 using System.Diagnostics;
22
23 namespace Tizen.NUI
24 {
25     /// <summary>
26     /// Mechanism to issue simple periodic or one-shot events.<br />
27     /// Timer is provided for application developers to be able to issue
28     /// simple periodic or one-shot events. Please note that the timer
29     /// callback functions should return as soon as possible because they
30     /// block the next SignalTick. Please note that timer signals are not
31     /// in sync with DALi's render timer.<br />
32     /// This class is a handle class so it can be stack allocated and used
33     /// as a member.<br />
34     /// </summary>
35     /// <since_tizen> 3 </since_tizen>
36     public class Timer : BaseHandle
37     {
38         private bool played = false;
39         private EventHandlerWithReturnType<object, TickEventArgs, bool> timerTickEventHandler;
40         private TickCallbackDelegate timerTickCallbackDelegate;
41
42         private System.IntPtr timerTickCallbackOfNative;
43
44         /// <summary>
45         /// Creates a tick timer that emits periodic signal.
46         /// </summary>
47         /// <param name="milliSec">Interval in milliseconds.</param>
48         /// <returns>A new timer.</returns>
49         /// <since_tizen> 3 </since_tizen>
50         public Timer(uint milliSec) : this(Interop.Timer.New(milliSec), true)
51         {
52             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
53
54             NUILog.Debug($"(0x{SwigCPtr.Handle:X})  Timer({milliSec}) Constructor!");
55         }
56         internal Timer(Timer timer) : this(Interop.Timer.NewTimer(Timer.getCPtr(timer)), true)
57         {
58             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
59         }
60
61         internal Timer(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
62         {
63             timerTickCallbackDelegate = OnTick;
64             timerTickCallbackOfNative = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(timerTickCallbackDelegate);
65
66             NUILog.Debug($"(0x{SwigCPtr.Handle:X})Timer() constructor!");
67         }
68
69         /// <summary>
70         /// Destructor.
71         /// </summary>
72         ~Timer()
73         {
74             Tizen.Log.Debug("NUI", $"(0x{SwigCPtr.Handle:X})Timer() destructor!, disposed={disposed}");
75         }
76
77         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
78         private delegate bool TickCallbackDelegate();
79
80         /// <summary>
81         /// @brief Event for the ticked signal, which can be used to subscribe or unsubscribe the event handler
82         /// provided by the user. The ticked signal is emitted after specified time interval.<br />
83         /// </summary>
84         /// <since_tizen> 3 </since_tizen>
85         public event EventHandlerWithReturnType<object, TickEventArgs, bool> Tick
86         {
87             add
88             {
89                 if (timerTickEventHandler == null && disposed == false)
90                 {
91                     TickSignal().Connect(timerTickCallbackOfNative);
92                 }
93                 timerTickEventHandler += value;
94             }
95             remove
96             {
97                 timerTickEventHandler -= value;
98                 if (timerTickEventHandler == null && TickSignal().Empty() == false)
99                 {
100                     TickSignal().Disconnect(timerTickCallbackOfNative);
101                 }
102             }
103         }
104
105         /// <summary>
106         /// Gets/Sets the interval of the timer.
107         /// </summary>
108         /// <remarks>For setter, this sets a new interval on the timer and starts the timer. <br />
109         /// Cancels the previous timer.
110         /// </remarks>
111         /// <since_tizen> 4 </since_tizen>
112         public uint Interval
113         {
114             get
115             {
116                 return GetInterval();
117             }
118             set
119             {
120                 SetInterval(value);
121             }
122         }
123
124         /// <summary>
125         /// Starts the timer.<br />
126         /// In case a timer is already running, its time is reset and the timer is restarted.<br />
127         /// </summary>
128         /// <since_tizen> 3 </since_tizen>
129         public void Start()
130         {
131             if (Thread.CurrentThread.ManagedThreadId != 1)
132             {
133                 Tizen.Log.Error("NUI", "current threadID : " + Thread.CurrentThread.ManagedThreadId);
134
135                 StackTrace st = new StackTrace(true);
136                 for (int i = 0; i < st.FrameCount; i++)
137                 {
138                     StackFrame sf = st.GetFrame(i);
139                     Tizen.Log.Error("NUI", " Method " + sf.GetMethod());
140                 }
141             }
142
143             if (SwigCPtr.Handle == global::System.IntPtr.Zero || disposed)
144             {
145                 NUILog.Error("[ERR] already disposed! can not get this done! just return here! please make sure that the handle gets free when using explicit Dispose()! For example, timer.Dispose(); timer = null; this must be done!");
146                 return;
147             }
148
149             played = true;
150             Interop.Timer.Start(SwigCPtr);
151
152             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
153         }
154
155         /// <summary>
156         /// Stops the timer.
157         /// </summary>
158         /// <since_tizen> 3 </since_tizen>
159         public void Stop()
160         {
161             if (Thread.CurrentThread.ManagedThreadId != 1)
162             {
163                 Tizen.Log.Error("NUI", "current threadID : " + Thread.CurrentThread.ManagedThreadId);
164
165                 StackTrace st = new StackTrace(true);
166                 for (int i = 0; i < st.FrameCount; i++)
167                 {
168                     StackFrame sf = st.GetFrame(i);
169                     Tizen.Log.Error("NUI", " Method " + sf.GetMethod());
170                 }
171             }
172
173             if (SwigCPtr.Handle == global::System.IntPtr.Zero || disposed)
174             {
175                 NUILog.Error("[ERR] already disposed! can not get this done! just return here! please make sure that the handle gets free when using explicit Dispose()! For example, timer.Dispose(); timer = null; this must be done!");
176                 return;
177             }
178
179             played = false;
180             Interop.Timer.Stop(SwigCPtr);
181
182             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
183         }
184
185         /// <summary>
186         /// Tells whether the timer is running.
187         /// </summary>
188         /// <returns>Whether the timer is started or not.</returns>
189         /// <since_tizen> 3 </since_tizen>
190         public bool IsRunning()
191         {
192             if (Thread.CurrentThread.ManagedThreadId != 1)
193             {
194                 Tizen.Log.Error("NUI", "current threadID : " + Thread.CurrentThread.ManagedThreadId);
195
196                 StackTrace st = new StackTrace(true);
197                 for (int i = 0; i < st.FrameCount; i++)
198                 {
199                     StackFrame sf = st.GetFrame(i);
200                     Tizen.Log.Error("NUI", " Method " + sf.GetMethod());
201                 }
202             }
203
204             if (SwigCPtr.Handle == global::System.IntPtr.Zero || disposed)
205             {
206                 NUILog.Error("[ERR] already disposed! can not get this done! just return here! please make sure that the handle gets free when using explicit Dispose()! For example, timer.Dispose(); timer = null; this must be done!");
207                 return false;
208             }
209
210             bool ret = Interop.Timer.IsRunning(SwigCPtr);
211             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
212             return ret;
213         }
214
215         /// <summary>
216         /// Sets a new interval on the timer and starts the timer.<br />
217         /// Cancels the previous timer.<br />
218         /// </summary>
219         /// <param name="milliSec">MilliSec interval in milliseconds.</param>
220         internal void SetInterval(uint milliSec)
221         {
222             NUILog.Debug($"(0x{SwigCPtr.Handle:X})SetInterval({milliSec})");
223
224             if (SwigCPtr.Handle == global::System.IntPtr.Zero || disposed)
225             {
226                 NUILog.Error("[ERR] already disposed! can not get this done! just return here! please make sure that the handle gets free when using explicit Dispose()! For example, timer.Dispose(); timer = null; this must be done!");
227                 return;
228             }
229
230             played = true;
231
232             Interop.Timer.SetInterval(SwigCPtr, milliSec);
233             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
234         }
235
236         internal uint GetInterval()
237         {
238             if (SwigCPtr.Handle == global::System.IntPtr.Zero || disposed)
239             {
240                 NUILog.Error("[ERR] already disposed! can not get this done! just return here! please make sure that the handle gets free when using explicit Dispose()! For example, timer.Dispose(); timer = null; this must be done!");
241                 return 0;
242             }
243
244             uint ret = Interop.Timer.GetInterval(SwigCPtr);
245             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
246             return ret;
247         }
248
249         internal TimerSignalType TickSignal()
250         {
251             TimerSignalType ret = new TimerSignalType(Interop.Timer.TickSignal(SwigCPtr), false);
252             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
253             return ret;
254         }
255
256         /// <summary>
257         /// Dispose.
258         /// </summary>
259         /// <since_tizen> 3 </since_tizen>
260         protected override void Dispose(DisposeTypes type)
261         {
262             NUILog.Debug($"(0x{SwigCPtr.Handle:X}) Timer.Dispose(type={type}, disposed={disposed})");
263
264             if (this != null && timerTickCallbackDelegate != null)
265             {
266                 TickSignal().Disconnect(timerTickCallbackOfNative);
267             }
268
269             if (disposed)
270             {
271                 return;
272             }
273
274             played = false;
275             base.Dispose(type);
276         }
277
278         /// This will not be public opened.
279         [EditorBrowsable(EditorBrowsableState.Never)]
280         protected override void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
281         {
282             Interop.Timer.DeleteTimer(swigCPtr);
283         }
284
285         private bool OnTick()
286         {
287             TickEventArgs e = new TickEventArgs();
288
289             if (played == false)
290             {
291                 Tizen.Log.Fatal("NUI", $"(0x{SwigCPtr.Handle:X}) OnTick() is called even played is false!");
292                 //throw new System.InvalidOperationException($"OnTick() exception!");
293             }
294
295             if (timerTickEventHandler != null && played == true)
296             {
297                 //here we send all data to user event handlers
298                 return timerTickEventHandler(this, e);
299             }
300             return false;
301         }
302
303         /// <summary>
304         /// Event arguments that passed via the tick event.
305         /// </summary>
306         /// <since_tizen> 3 </since_tizen>
307         public class TickEventArgs : EventArgs
308         {
309         }
310     }
311 }