2 * Copyright(c) 2021 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;
25 /// BaseHandle is a handle to an internal Dali resource.
27 /// <since_tizen> 3 </since_tizen>
28 public class BaseHandle : Element, global::System.IDisposable
33 /// <since_tizen> 3 </since_tizen>
34 [Obsolete("Deprecated in API9, Will be removed in API11, Please use SwigCMemOwn")]
35 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
36 protected bool swigCMemOwn;
39 /// A flag to check if it is already disposed.
41 /// <since_tizen> 3 </since_tizen>
42 [Obsolete("Deprecated in API9, Will be removed in API11, Please use Disposed")]
43 [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
44 protected bool disposed = false;
46 private global::System.Runtime.InteropServices.HandleRef swigCPtr;
47 private global::System.Runtime.InteropServices.HandleRef swigCPtrCopy;
48 private bool registerMe;
50 //A 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 // using copy constructor to create another native handle so Registry.Unregister works fine.
82 swigCPtrCopy = new global::System.Runtime.InteropServices.HandleRef(this, Interop.BaseHandle.NewBaseHandle(swigCPtr));
83 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
87 // Register this instance of BaseHandle in the registry.
88 Registry.Register(this);
91 DebugFileLogging.Instance.WriteLog($"type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")}");
92 if (this is BaseComponents.View view)
94 DebugFileLogging.Instance.WriteLog($"ID:{view.ID} Name:{view.Name}");
96 DebugFileLogging.Instance.WriteLog($" BaseHandle.contructor with cMemeryOwn END");
97 DebugFileLogging.Instance.WriteLog($"=============================");
100 internal BaseHandle(global::System.IntPtr cPtr)
102 DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor START");
104 registerMe = swigCMemOwn = true;
106 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
108 // using copy constructor to create another native handle so Registry.Unregister works fine.
109 swigCPtrCopy = new global::System.Runtime.InteropServices.HandleRef(this, Interop.BaseHandle.NewBaseHandle(SwigCPtr));
110 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
114 // Register this instance of BaseHandle in the registry.
115 Registry.Register(this);
118 DebugFileLogging.Instance.WriteLog($"type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")}");
119 if (this is BaseComponents.View view)
121 DebugFileLogging.Instance.WriteLog($"ID:{view.ID} Name:{view.Name}");
123 DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor END");
124 DebugFileLogging.Instance.WriteLog($"=============================");
130 /// <since_tizen> 3 </since_tizen>
131 // following this guide: https://docs.microsoft.com/ko-kr/dotnet/fundamentals/code-analysis/quality-rules/ca1063?view=vs-2019 (CA1063)
132 ~BaseHandle() => Dispose(false);
135 /// Event when a property is set.
137 /// <since_tizen> 5 </since_tizen>
138 /// <seealso cref="BindableObject.PropertyChanged"/>
139 [Obsolete("Deprecated in API9, Will be removed in API11, Please use BindableObject.PropertyChanged instead!")]
140 public event PropertyChangedEventHandler PropertySet;
142 internal global::System.Runtime.InteropServices.HandleRef GetBaseHandleCPtrHandleRef
151 /// Returns the bool value true to indicate that an operand is true and returns false otherwise.
153 /// <since_tizen> 3 </since_tizen>
154 public static bool operator true(BaseHandle handle)
156 // if the C# object is null, return false
157 if (BaseHandle.ReferenceEquals(handle, null))
161 // returns true if the handle has a body, false otherwise
162 return handle.HasBody();
166 /// Returns the bool false to indicate that an operand is false and returns true otherwise.
168 /// <since_tizen> 3 </since_tizen>
169 public static bool operator false(BaseHandle handle)
171 // if the C# object is null, return true
172 if (BaseHandle.ReferenceEquals(handle, null))
176 return !handle.HasBody();
180 /// Explicit conversion from Handle to bool.
182 /// <since_tizen> 3 </since_tizen>
183 public static explicit operator bool(BaseHandle handle)
185 // if the C# object is null, return false
186 if (BaseHandle.ReferenceEquals(handle, null))
190 // returns true if the handle has a body, false otherwise
191 return handle.HasBody();
195 /// Equality operator
197 /// <since_tizen> 3 </since_tizen>
198 public static bool operator ==(BaseHandle x, BaseHandle y)
200 // if the C# objects are the same return true
201 if (BaseHandle.ReferenceEquals(x, y))
205 if (!BaseHandle.ReferenceEquals(x, null) && !BaseHandle.ReferenceEquals(y, null))
207 // drop into native code to see if both handles point to the same body
211 if (BaseHandle.ReferenceEquals(x, null) && !BaseHandle.ReferenceEquals(y, null))
213 if (y.HasBody()) return false;
216 if (!BaseHandle.ReferenceEquals(x, null) && BaseHandle.ReferenceEquals(y, null))
218 if (x.HasBody()) return false;
226 /// Inequality operator. Returns Null if either operand is Null
228 /// <since_tizen> 3 </since_tizen>
229 public static bool operator !=(BaseHandle x, BaseHandle y)
235 /// Logical AND operator.<br />
236 /// It's possible when doing a operator this function (opBitwiseAnd) is never called due to short circuiting.<br />
238 /// <since_tizen> 3 </since_tizen>
239 public static BaseHandle operator &(BaseHandle x, BaseHandle y)
249 /// Logical OR operator for ||.<br />
250 /// It's possible when doing a || this function (opBitwiseOr) is never called due to short circuiting.<br />
252 /// <since_tizen> 3 </since_tizen>
253 public static BaseHandle operator |(BaseHandle x, BaseHandle y)
255 if (!BaseHandle.ReferenceEquals(x, null) || !BaseHandle.ReferenceEquals(y, null))
257 if (x != null && x.HasBody())
261 if (y != null && y.HasBody())
271 /// Logical ! operator
273 /// <since_tizen> 3 </since_tizen>
274 public static bool operator !(BaseHandle x)
276 // if the C# object is null, return true
277 if (BaseHandle.ReferenceEquals(x, null))
291 /// <param name="o">The object should be compared.</param>
292 /// <returns>True if equal.</returns>
293 /// <since_tizen> 5 </since_tizen>
294 public override bool Equals(object o)
296 return base.Equals(o);
300 /// Gets the hash code of this baseHandle.
302 /// <returns>The hash code.</returns>
303 /// <since_tizen> 5 </since_tizen>
304 public override int GetHashCode()
306 return base.GetHashCode();
312 /// <since_tizen> 3 </since_tizen>
313 public void Dispose()
317 Dispose(DisposeTypes.Implicit);
323 GC.SuppressFinalize(this);
327 /// Hidden API (Inhouse API).
331 /// Following the guide of https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose.
332 /// This will replace "protected virtual void Dispose(DisposeTypes type)" which is exactly same in functionality.
334 /// <param name="disposing">true in order to free managed objects</param>
335 // Protected implementation of Dispose pattern.
336 [EditorBrowsable(EditorBrowsableState.Never)]
337 protected virtual void Dispose(bool disposing)
346 // TODO: dispose managed state (managed objects).
347 // Explicit call. user calls Dispose()
349 //Throw exception if Dispose() is called in separate thread.
350 if (!Window.IsInstalled())
352 var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
353 var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
354 var me = this.GetType().FullName;
356 DebugFileLogging.Instance.WriteLog("[NUI][BaseHandle] This API called from separate thread. This API must be called from MainThread. \n" +
357 $" process:{process} thread:{thread}, disposing:{disposing}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
359 throw new global::System.InvalidOperationException("[NUI][BaseHandle] This API called from separate thread. This API must be called from MainThread. \n" +
360 $" process:{process} thread:{thread}, disposing:{disposing}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
365 DebugFileLogging.Instance.WriteLog($"should not be here! (dead code) this will be removed!");
367 throw new global::System.Exception($"[NUI] should not be here! (dead code) this will be removed!");
368 Dispose(DisposeTypes.Implicit);
372 Dispose(DisposeTypes.Explicit);
377 // Implicit call. user doesn't call Dispose(), so this object is added into DisposeQueue to be disposed automatically.
378 if (!isDisposeQueued)
380 isDisposeQueued = true;
381 DisposeQueue.Instance.Add(this);
385 // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
386 // TODO: set large fields to null.
391 /// Performs an action on this object with the given action name and attributes.
393 /// <param name="actionName">The command for the action.</param>
394 /// <param name="attributes">The list of attributes for the action.</param>
395 /// <returns>The action is performed by the object or not.</returns>
396 /// <since_tizen> 3 </since_tizen>
397 public bool DoAction(string actionName, PropertyMap attributes)
399 bool ret = Interop.BaseHandle.DoAction(swigCPtrCopy, actionName, PropertyMap.getCPtr(attributes));
400 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
405 /// Returns the type name for the Handle.<br />
406 /// Will return an empty string if the typename does not exist. This will happen for types that
407 /// have not registered with type-registry.
409 /// <returns>The type name. Empty string if the typename does not exist.</returns>
410 /// <since_tizen> 3 </since_tizen>
411 public string GetTypeName()
413 string ret = Interop.BaseHandle.GetTypeName(swigCPtrCopy);
414 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
419 /// Returns the type info for the Handle.<br />
421 /// <param name="info">The type information.</param>
422 /// <returns>True If get the type info.</returns>
423 /// <since_tizen> 3 </since_tizen>
424 public bool GetTypeInfo(Tizen.NUI.TypeInfo info)
426 bool ret = Interop.BaseHandle.GetTypeInfo(swigCPtrCopy, Tizen.NUI.TypeInfo.getCPtr(info));
427 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
432 /// Resets the handle.
434 /// <since_tizen> 3 </since_tizen>
437 Interop.BaseHandle.Reset(swigCPtrCopy);
438 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
442 /// To check the BaseHandle instance is equal or not.
444 /// <param name="rhs">The baseHandle instance.</param>
445 /// <returns>True If equal.</returns>
446 /// <since_tizen> 3 </since_tizen>
447 public bool EqualTo(BaseHandle rhs)
449 bool ret = Interop.BaseHandle.EqualTo(swigCPtrCopy, BaseHandle.getCPtr(rhs));
450 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
455 /// To check the BaseHandle instance is equal or not.
457 /// <param name="rhs">The baseHandle instance.</param>
458 /// <returns>True If not equal.</returns>
459 /// <since_tizen> 3 </since_tizen>
460 public bool NotEqualTo(BaseHandle rhs)
462 bool ret = Interop.BaseHandle.NotEqualTo(swigCPtrCopy, BaseHandle.getCPtr(rhs));
463 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
468 /// To check the BaseHandle instance has body or not.
470 /// <returns>True If the baseHandle instance has body.</returns>
471 /// <since_tizen> 3 </since_tizen>
472 public bool HasBody()
474 if (swigCPtrCopy.Handle == IntPtr.Zero)
479 if (disposed == true)
483 bool ret = Interop.BaseHandle.HasBody(swigCPtrCopy);
484 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
489 /// To check the BaseHandle instance is equal or not.
491 /// <param name="rhs">The baseHandle instance.</param>
492 /// <returns>True If equal.</returns>
493 /// <since_tizen> 3 </since_tizen>
494 public bool IsEqual(BaseHandle rhs)
496 if (disposed == true)
501 bool ret = Interop.BaseHandle.IsEqual(swigCPtrCopy, BaseHandle.getCPtr(rhs));
502 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
506 internal static global::System.Runtime.InteropServices.HandleRef getCPtr(BaseHandle obj)
508 return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtrCopy;
511 internal void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
513 PropertySet?.Invoke(this, new PropertyChangedEventArgs(propertyName));
519 /// <since_tizen> 3 </since_tizen>
520 protected virtual void Dispose(DisposeTypes type)
527 DebugFileLogging.Instance.WriteLog($"BaseHandle.Dispose({type}) START");
529 if (type == DisposeTypes.Explicit)
532 //Release your own managed resources here.
533 //You should release all of your own disposable objects here.
537 //Release your own unmanaged resources here.
538 //You should not access any managed member here except static instance.
539 //because the execution order of Finalizes is non-deterministic.
541 //Unreference this instance from Registry.
544 Registry.Unregister(this);
547 DebugFileLogging.Instance.WriteLog($"swigCMemOwn:{swigCMemOwn} type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")}");
549 // this is temporary test code. will be removed laster
551 if (swigCPtr.Handle != IntPtr.Zero && swigCPtrCopy.Handle != IntPtr.Zero)
553 var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
554 var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
555 var me = this.GetType().FullName;
556 var daliId = "unknown";
557 var hash = this.GetType().GetHashCode();
558 var name = "unknown";
560 if (this is BaseComponents.View)
562 daliId = Interop.Actor.GetId(swigCPtrCopy).ToString();
563 name = Interop.Actor.GetName(swigCPtrCopy);
564 BaseObject tmp = new BaseObject(Interop.BaseHandle.GetBaseObject(swigCPtrCopy), false);
565 var refCnt = tmp.ReferenceCount();
569 DebugFileLogging.Instance.WriteLog($"[ERR] reference count is over than 2. Could be a memory leak. Need to check! \n" +
570 $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me} \n" +
571 $" disposeType:{type}, name:{name}, daliID:{daliId}, hash:{hash}, refCnt:{refCnt}");
573 Log.Error("NUI", $"[ERR] reference count is over than 2. Could be a memory leak. Need to check! \n" +
574 $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me} \n" +
575 $" disposeType:{type}, name:{name}, daliID:{daliId}, hash:{hash}, refCnt:{refCnt}");
581 if (SwigCPtr.Handle != IntPtr.Zero)
586 ReleaseSwigCPtr(SwigCPtr);
588 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
592 var me = this.GetType().FullName;
593 DebugFileLogging.Instance.WriteLog($"[ERR] SwigCPtr is NULL, need to check! me:{me}");
594 Log.Error("NUI", $"[ERR] SwigCPtr is NULL, need to check! me:{me}");
597 if (swigCPtrCopy.Handle != global::System.IntPtr.Zero)
600 Interop.BaseHandle.DeleteBaseHandle(swigCPtrCopy);
601 swigCPtrCopy = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
605 var me = this.GetType().FullName;
606 DebugFileLogging.Instance.WriteLog($"[ERR] swigCPtrCopy is NULL, need to check! me:{me}");
607 Log.Error("NUI", $"[ERR] swigCPtrCopy is NULL, need to check! me:{me}");
612 if (null != Application.Current)
614 Application.Current.XamlResourceChanged -= OnResourcesChanged;
616 DebugFileLogging.Instance.WriteLog($"BaseHandle.Dispose({type}) END");
617 DebugFileLogging.Instance.WriteLog($"=============================");
618 NUILog.Debug($"BaseHandle.Dispose({type}) END");
619 NUILog.Debug($"=============================");
623 /// Release swigCPtr.
625 /// <since_tizen> 6 </since_tizen>
626 /// This will not be public opened.
627 [EditorBrowsable(EditorBrowsableState.Never)]
628 protected virtual void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
633 /// Contains event arguments for the FocusChangeRequested event.
635 [Obsolete("Deprecated in API9; Will be removed in API11.")]
636 public class FocusRequestArgs : EventArgs
640 /// Gets or sets a value that indicates the starting focus state of the element for which a focus change is requested.
642 public bool Focus { get; set; }
645 /// Gets or sets a value that indicates the ending focus state of the element for which a focus change is requested.
647 public bool Result { get; set; }
650 internal global::System.Runtime.InteropServices.HandleRef SwigCPtr
654 if (swigCPtr.Handle == IntPtr.Zero)
656 var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
657 var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
658 var me = this.GetType().FullName;
660 throw new ObjectDisposedException(nameof(SwigCPtr), $"Error! NUI's native dali object is already disposed. " +
661 $"OR the native dali object handle of NUI becomes null! \n" +
662 $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
668 internal bool IsNativeHandleInvalid()
670 return swigCPtr.Handle == IntPtr.Zero;
676 [EditorBrowsable(EditorBrowsableState.Never)]
677 protected internal bool SwigCMemOwn => swigCMemOwn;
680 /// A flag to check if it is already disposed.
682 [EditorBrowsable(EditorBrowsableState.Never)]
683 protected internal bool Disposed => disposed;
686 /// A flag to check if it is disposed by DisposeQueue.
688 [EditorBrowsable(EditorBrowsableState.Never)]
689 protected internal bool IsDisposeQueued => isDisposeQueued;