2 * Copyright(c) 2022 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.
18 using System.ComponentModel;
19 using System.Runtime.CompilerServices;
20 using Tizen.NUI.Binding;
21 using global::System.Diagnostics;
26 /// BaseHandle is a handle to an internal Dali resource.
28 /// <since_tizen> 3 </since_tizen>
29 public class BaseHandle : Element, global::System.IDisposable
34 /// <since_tizen> 3 </since_tizen>
35 [Obsolete("Deprecated in API9, will be removed in API11, Use SwigCMemOwn")]
36 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
37 protected bool swigCMemOwn;
40 /// The flag to check if it is already disposed of.
42 /// <since_tizen> 3 </since_tizen>
43 [Obsolete("Deprecated in API9, will be removed in API11, Use Disposed")]
44 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
45 protected bool disposed = false;
47 private global::System.Runtime.InteropServices.HandleRef swigCPtr;
48 private bool registerMe;
50 //The flag to check who called Dispose(). (By User or DisposeQueue)
51 private bool isDisposeQueued = false;
54 /// Create an instance of BaseHandle.
56 /// <since_tizen> 3 </since_tizen>
57 public BaseHandle() : this(Interop.BaseHandle.NewBaseHandle())
59 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
63 /// Create an instance of BaseHandle.
65 /// <param name="handle">The BaseHandle instance.</param>
66 /// <since_tizen> 3 </since_tizen>
67 public BaseHandle(BaseHandle handle) : this(Interop.BaseHandle.NewBaseHandle(BaseHandle.getCPtr(handle)))
69 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
72 internal BaseHandle(global::System.IntPtr cPtr, bool cMemoryOwn)
74 //to catch derived classes dali native exceptions
75 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
77 DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor with cMemeryOwn:{cMemoryOwn} START");
79 registerMe = swigCMemOwn = cMemoryOwn;
80 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
81 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
85 // Register this instance of BaseHandle in the registry.
86 Registry.Register(this);
89 disposeDebuggingCtor();
90 DebugFileLogging.Instance.WriteLog($" BaseHandle.contructor with cMemeryOwn END");
91 DebugFileLogging.Instance.WriteLog($"=============================");
94 internal BaseHandle(global::System.IntPtr cPtr)
96 DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor START");
98 registerMe = swigCMemOwn = true;
100 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
101 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
105 // Register this instance of BaseHandle in the registry.
106 Registry.Register(this);
109 disposeDebuggingCtor();
110 DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor END");
111 DebugFileLogging.Instance.WriteLog($"=============================");
117 /// <since_tizen> 3 </since_tizen>
118 // following this guide: https://docs.microsoft.com/ko-kr/dotnet/fundamentals/code-analysis/quality-rules/ca1063?view=vs-2019 (CA1063)
119 ~BaseHandle() => Dispose(false);
122 /// Event when a property is set.
124 /// <since_tizen> 5 </since_tizen>
125 /// <seealso cref="BindableObject.PropertyChanged"/>
126 [Obsolete("Deprecated in API9, will be removed in API11, Use BindableObject.PropertyChanged instead.")]
127 public event PropertyChangedEventHandler PropertySet;
129 internal global::System.Runtime.InteropServices.HandleRef GetBaseHandleCPtrHandleRef
138 /// Returns the bool value true to indicate that an operand is true and returns false otherwise.
140 /// <since_tizen> 3 </since_tizen>
141 public static bool operator true(BaseHandle handle)
143 // if the C# object is null, return false
144 if (BaseHandle.ReferenceEquals(handle, null))
148 // returns true if the handle has a body, false otherwise
149 return handle.HasBody();
153 /// Returns the bool false to indicate that an operand is false and returns true otherwise.
155 /// <since_tizen> 3 </since_tizen>
156 public static bool operator false(BaseHandle handle)
158 // if the C# object is null, return true
159 if (BaseHandle.ReferenceEquals(handle, null))
163 return !handle.HasBody();
167 /// Explicit conversion from Handle to bool.
169 /// <since_tizen> 3 </since_tizen>
170 public static explicit operator bool(BaseHandle handle)
172 // if the C# object is null, return false
173 if (BaseHandle.ReferenceEquals(handle, null))
177 // returns true if the handle has a body, false otherwise
178 return handle.HasBody();
182 /// Equality operator
184 /// <since_tizen> 3 </since_tizen>
185 public static bool operator ==(BaseHandle x, BaseHandle y)
187 // if the C# objects are the same return true
188 if (BaseHandle.ReferenceEquals(x, y))
192 if (!BaseHandle.ReferenceEquals(x, null) && !BaseHandle.ReferenceEquals(y, null))
194 // drop into native code to see if both handles point to the same body
198 if (BaseHandle.ReferenceEquals(x, null) && !BaseHandle.ReferenceEquals(y, null))
200 if (y.HasBody()) return false;
203 if (!BaseHandle.ReferenceEquals(x, null) && BaseHandle.ReferenceEquals(y, null))
205 if (x.HasBody()) return false;
213 /// Inequality operator. Returns Null if either operand is Null
215 /// <since_tizen> 3 </since_tizen>
216 public static bool operator !=(BaseHandle x, BaseHandle y)
222 /// Logical AND operator.<br />
223 /// It's possible when doing a operator this function (opBitwiseAnd) is never called due to short circuiting.<br />
225 /// <since_tizen> 3 </since_tizen>
226 public static BaseHandle operator &(BaseHandle x, BaseHandle y)
236 /// Logical OR operator for ||.<br />
237 /// It's possible when doing a || this function (opBitwiseOr) is never called due to short circuiting.<br />
239 /// <since_tizen> 3 </since_tizen>
240 public static BaseHandle operator |(BaseHandle x, BaseHandle y)
242 if (!BaseHandle.ReferenceEquals(x, null) || !BaseHandle.ReferenceEquals(y, null))
244 if (x != null && x.HasBody())
248 if (y != null && y.HasBody())
258 /// Logical ! operator
260 /// <since_tizen> 3 </since_tizen>
261 public static bool operator !(BaseHandle x)
263 // if the C# object is null, return true
264 if (BaseHandle.ReferenceEquals(x, null))
278 /// <param name="o">The object should be compared.</param>
279 /// <returns>True if equal.</returns>
280 /// <since_tizen> 5 </since_tizen>
281 public override bool Equals(object o)
283 return base.Equals(o);
287 /// Gets the hash code of this baseHandle.
289 /// <returns>The hash code.</returns>
290 /// <since_tizen> 5 </since_tizen>
291 public override int GetHashCode()
293 return base.GetHashCode();
299 /// <since_tizen> 3 </since_tizen>
300 public void Dispose()
304 Dispose(DisposeTypes.Implicit);
310 GC.SuppressFinalize(this);
314 /// Hidden API (Inhouse API).
318 /// Following the guide of https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose.
319 /// This will replace "protected virtual void Dispose(DisposeTypes type)" which is exactly same in functionality.
321 /// <param name="disposing">true in order to free managed objects</param>
322 // Protected implementation of Dispose pattern.
323 [EditorBrowsable(EditorBrowsableState.Never)]
324 protected virtual void Dispose(bool disposing)
333 // TODO: dispose managed state (managed objects).
334 // Explicit call. user calls Dispose()
336 //Throw exception if Dispose() is called in separate thread.
337 if (!Window.IsInstalled())
339 using var process = global::System.Diagnostics.Process.GetCurrentProcess();
340 var processId = process.Id;
341 var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
342 var me = this.GetType().FullName;
344 DebugFileLogging.Instance.WriteLog("[NUI][BaseHandle] This API called from separate thread. This API must be called from MainThread. \n" +
345 $" process:{processId} thread:{thread}, disposing:{disposing}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
347 Tizen.Log.Fatal("NUI", $"[NUI][BaseHandle] This API called from separate thread. This API must be called from MainThread. \n" +
348 $" process:{processId} thread:{thread}, disposing:{disposing}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
350 //to fix ArtApp black screen issue. this will be enabled after talking with ArtApp team and fixing it.
351 // throw new global::System.InvalidOperationException("[NUI][BaseHandle] This API called from separate thread. This API must be called from MainThread. \n" +
352 // $" process:{process} thread:{thread}, disposing:{disposing}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
357 DebugFileLogging.Instance.WriteLog($"should not be here! (dead code) this will be removed!");
359 Tizen.Log.Fatal("NUI", $"[NUI] should not be here! (dead code) this will be removed!");
361 //to fix ArtApp black screen issue. this will be enabled after talking with ArtApp team and fixing it.
362 // throw new global::System.Exception($"[NUI] should not be here! (dead code) this will be removed!");
363 Dispose(DisposeTypes.Implicit);
367 Dispose(DisposeTypes.Explicit);
372 // Implicit call. user doesn't call Dispose(), so this object is added into DisposeQueue to be disposed automatically.
373 if (!isDisposeQueued)
375 isDisposeQueued = true;
376 DisposeQueue.Instance.Add(this);
380 // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
381 // TODO: set large fields to null.
386 /// Performs an action on this object with the given action name and attributes.
388 /// <param name="actionName">The command for the action.</param>
389 /// <param name="attributes">The list of attributes for the action.</param>
390 /// <returns>The action is performed by the object or not.</returns>
391 /// <since_tizen> 3 </since_tizen>
392 public bool DoAction(string actionName, PropertyMap attributes)
394 bool ret = Interop.BaseHandle.DoAction(swigCPtr, actionName, PropertyMap.getCPtr(attributes));
395 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
400 /// Returns the type name for the Handle.<br />
401 /// Will return an empty string if the typename does not exist. This will happen for types that
402 /// have not registered with type-registry.
404 /// <returns>The type name. Empty string if the typename does not exist.</returns>
405 /// <since_tizen> 3 </since_tizen>
406 public string GetTypeName()
408 string ret = Interop.BaseHandle.GetTypeName(swigCPtr);
409 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
414 /// Returns the type info for the Handle.<br />
416 /// <param name="info">The type information.</param>
417 /// <returns>True If get the type info.</returns>
418 /// <since_tizen> 3 </since_tizen>
419 public bool GetTypeInfo(Tizen.NUI.TypeInfo info)
421 bool ret = Interop.BaseHandle.GetTypeInfo(swigCPtr, Tizen.NUI.TypeInfo.getCPtr(info));
422 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
427 /// Resets the handle.
429 /// <since_tizen> 3 </since_tizen>
431 /// This will be deprecated, please use Dispose() instead.
436 NUILog.Error("[ERROR] This(BaseHandle.Reset) will be deprecated, please use Dispose() instead!");
440 /// To check the BaseHandle instance is equal or not.
442 /// <param name="rhs">The baseHandle instance.</param>
443 /// <returns>True If equal.</returns>
444 /// <since_tizen> 3 </since_tizen>
445 public bool EqualTo(BaseHandle rhs)
447 bool ret = Interop.BaseHandle.EqualTo(swigCPtr, BaseHandle.getCPtr(rhs));
448 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
453 /// To check the BaseHandle instance is equal or not.
455 /// <param name="rhs">The baseHandle instance.</param>
456 /// <returns>True If not equal.</returns>
457 /// <since_tizen> 3 </since_tizen>
458 public bool NotEqualTo(BaseHandle rhs)
460 bool ret = Interop.BaseHandle.NotEqualTo(swigCPtr, BaseHandle.getCPtr(rhs));
461 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
466 /// To check the BaseHandle instance has body or not.
468 /// <returns>True If the baseHandle instance has body.</returns>
469 /// <since_tizen> 3 </since_tizen>
470 public bool HasBody()
472 if (swigCPtr.Handle == IntPtr.Zero)
477 if (disposed == true)
481 bool ret = Interop.BaseHandle.HasBody(swigCPtr);
482 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
487 /// To check the BaseHandle instance is equal or not.
489 /// <param name="rhs">The baseHandle instance.</param>
490 /// <returns>True If equal.</returns>
491 /// <since_tizen> 3 </since_tizen>
492 public bool IsEqual(BaseHandle rhs)
494 if (disposed == true || rhs == null || !rhs.HasBody())
499 bool ret = Interop.BaseHandle.IsEqual(swigCPtr, BaseHandle.getCPtr(rhs));
500 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
504 internal static global::System.Runtime.InteropServices.HandleRef getCPtr(BaseHandle obj)
506 return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
509 internal void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
511 PropertySet?.Invoke(this, new PropertyChangedEventArgs(propertyName));
517 /// <since_tizen> 3 </since_tizen>
518 protected virtual void Dispose(DisposeTypes type)
525 DebugFileLogging.Instance.WriteLog($"BaseHandle.Dispose({type}) START");
527 if (type == DisposeTypes.Explicit)
530 //Release your own managed resources here.
531 //You should release all of your own disposable objects here.
535 //Release your own unmanaged resources here.
536 //You should not access any managed member here except static instance.
537 //because the execution order of Finalizes is non-deterministic.
539 //Unreference this instance from Registry.
542 Registry.Unregister(this);
545 disposeDebuggingDispose(type);
547 if (SwigCPtr.Handle != IntPtr.Zero)
549 var nativeSwigCPtr = swigCPtr.Handle;
550 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
554 ReleaseSwigCPtr(new global::System.Runtime.InteropServices.HandleRef(this, nativeSwigCPtr));
559 var me = this.GetType().FullName;
560 DebugFileLogging.Instance.WriteLog($"[ERR] SwigCPtr is NULL, need to check! me:{me}");
561 Log.Error("NUI", $"[ERR] SwigCPtr is NULL, need to check! me:{me}");
565 DebugFileLogging.Instance.WriteLog($"BaseHandle.Dispose({type}) END");
566 DebugFileLogging.Instance.WriteLog($"=============================");
567 NUILog.Debug($"BaseHandle.Dispose({type}) END");
568 NUILog.Debug($"=============================");
572 /// Release swigCPtr.
574 /// <since_tizen> 6 </since_tizen>
575 /// This will not be public opened.
576 [EditorBrowsable(EditorBrowsableState.Never)]
577 protected virtual void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
579 Interop.BaseHandle.DeleteBaseHandle(swigCPtr.Handle);
583 /// Contains event arguments for the FocusChangeRequested event.
585 [Obsolete("Deprecated in API9; Will be removed in API11.")]
586 public class FocusRequestArgs : EventArgs
590 /// Gets or sets a value that indicates the starting focus state of the element for which a focus change is requested.
592 public bool Focus { get; set; }
595 /// Gets or sets a value that indicates the ending focus state of the element for which a focus change is requested.
597 public bool Result { get; set; }
600 internal global::System.Runtime.InteropServices.HandleRef SwigCPtr
604 if (swigCPtr.Handle == IntPtr.Zero)
606 using var process = global::System.Diagnostics.Process.GetCurrentProcess();
607 var processId = process.Id;
608 var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
609 var me = this.GetType().FullName;
611 Tizen.Log.Fatal("NUI", $"SwigCPtr Error! NUI's native dali object is already disposed. " +
612 $"OR the native dali object handle of NUI becomes null! \n" +
613 $" process:{processId} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
615 Tizen.Log.Fatal("NUI", $"[ERROR] back trace!");
616 global::System.Diagnostics.StackTrace st = new global::System.Diagnostics.StackTrace(true);
617 for (int i = 0; i < st.FrameCount; i++)
619 global::System.Diagnostics.StackFrame sf = st.GetFrame(i);
620 Tizen.Log.Fatal("NUI", " Method " + sf.GetMethod() + ":" + sf.GetFileName() + ":" + sf.GetFileLineNumber());
622 Tizen.Log.Fatal("NUI", "Error! just return here with null swigCPtr! this can cause unknown error or crash in next step");
624 //to fix ArtApp black screen issue. this will be enabled after talking with ArtApp team and fixing it.
625 // throw new ObjectDisposedException(nameof(SwigCPtr), $"Error! NUI's native dali object is already disposed. " +
626 // $"OR the native dali object handle of NUI becomes null! \n" +
627 // $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
633 internal bool IsNativeHandleInvalid()
635 return swigCPtr.Handle == IntPtr.Zero;
641 [EditorBrowsable(EditorBrowsableState.Never)]
642 protected internal bool SwigCMemOwn => swigCMemOwn;
645 /// The flag to check if it is already disposed of.
647 [EditorBrowsable(EditorBrowsableState.Never)]
648 protected internal bool Disposed => disposed;
651 /// The flag to check if it is disposed by DisposeQueue.
653 [EditorBrowsable(EditorBrowsableState.Never)]
654 protected internal bool IsDisposeQueued => isDisposeQueued;
656 [Conditional("NUI_DISPOSE_DEBUG_ON")]
657 private void disposeDebuggingCtor()
659 DebugFileLogging.Instance.WriteLog($"type:{GetType()} copyNativeHandle:{swigCPtr.Handle.ToString("X8")}");
660 if (this is BaseComponents.View view)
662 DebugFileLogging.Instance.WriteLog($"ID:{view.ID} Name:{view.Name}");
664 global::System.Diagnostics.StackTrace st = new global::System.Diagnostics.StackTrace(true);
665 for (int i = 0; i < st.FrameCount; i++)
667 global::System.Diagnostics.StackFrame sf = st.GetFrame(i);
668 DebugFileLogging.Instance.WriteLog($"[{i}] {sf.GetMethod()}:{sf.GetFileName()}:{sf.GetFileLineNumber()}");
673 [Conditional("NUI_DISPOSE_DEBUG_ON")]
674 private void disposeDebuggingDispose(DisposeTypes type)
676 DebugFileLogging.Instance.WriteLog($"swigCMemOwn:{swigCMemOwn} type:{GetType()} copyNativeHandle:{swigCPtr.Handle.ToString("X8")} HasBody:{HasBody()}");
680 using var currentProcess = global::System.Diagnostics.Process.GetCurrentProcess();
681 var process = currentProcess.Id;
682 var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
683 var me = this.GetType().FullName;
684 var daliId = "unknown";
685 var hash = this.GetType().GetHashCode();
686 var name = "unknown";
688 if (this is BaseComponents.View)
690 daliId = Interop.Actor.GetId(swigCPtr).ToString();
691 name = Interop.Actor.GetName(swigCPtr);
692 BaseObject tmp = new BaseObject(Interop.BaseHandle.GetBaseObject(swigCPtr), false);
693 var refCnt = tmp.ReferenceCount();
697 DebugFileLogging.Instance.WriteLog($"[ERR] reference count is over than 2. Could be a memory leak. Need to check! \n" +
698 $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me} \n" +
699 $" disposeType:{type}, name:{name}, daliID:{daliId}, hash:{hash}, refCnt:{refCnt}");
701 Log.Error("NUI", $"[ERR] reference count is over than 2. Could be a memory leak. Need to check! \n" +
702 $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me} \n" +
703 $" disposeType:{type}, name:{name}, daliID:{daliId}, hash:{hash}, refCnt:{refCnt}");