/* * Copyright 2019 by its authors. See AUTHORS. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Threading; using System.Reflection; using System.Collections; namespace Efl { namespace Eo { /// /// Abstract class that delivers base level binding to Efl Objects. /// /// Most of it is protected functionalities to serve the generated /// binding classes that inherit from it. /// public abstract class EoWrapper : IWrapper, IDisposable { /// Object used to synchronize access to EFL events. private readonly object eflBindingEventLock = new object(); private bool generated = true; private System.IntPtr handle = IntPtr.Zero; private static Efl.EventCb ownershipUniqueDelegate = new Efl.EventCb(OwnershipUniqueCallback); private static Efl.EventCb ownershipSharedDelegate = new Efl.EventCb(OwnershipSharedCallback); private Hashtable keyValueHash = null; /// Constructor to be used when objects are expected to be constructed from native code. /// For a class that inherited from an EFL# class to be properly constructed from native code /// one must create a constructor with this signature and calls this base constructor from it. /// This constructor will take care of calling base constructors of the native classes and /// perform additional setup so objects are ready to use. /// It is advisable to check for the property in the top level /// constructor and signal an error when it has a value of IntPtr.Zero after this /// constructor completion. /// Warning: Do not use this constructor directly from a `new` statement. /// Tag struct storing the native handle of the object being constructed. protected EoWrapper(ConstructingHandle ch) { generated = false; handle = Efl.Eo.Globals.efl_constructor(Efl.Eo.Globals.efl_super(ch.NativeHandle, Efl.Eo.Globals.efl_class_get(ch.NativeHandle))); if (handle == IntPtr.Zero) { Eina.Log.Warning("Natice constructor returned NULL"); return; } AddWrapperSupervisor(); // Make an additional reference to C# // - Will also call EVENT_OWNERSHIP_SHARED Efl.Eo.Globals.efl_ref(handle); } /// Initializes a new instance of the class. /// Internal usage: Constructs an instance from a native pointer. This is used when interacting with C code and should not be used directly. /// Do not implement this constructor. /// The native pointer to be wrapped. protected EoWrapper(Efl.Eo.Globals.WrappingHandle wh) { handle = wh.NativeHandle; AddWrapperSupervisor(); } /// Initializes a new instance of the class. /// Internal usage: Constructor to actually call the native library constructors. C# subclasses /// must use the public constructor only. /// The pointer to the base native Eo class. /// The Efl.Object parent of this instance. /// Name of the file from where the constructor is called. /// Number of the line from where the constructor is called. protected EoWrapper(IntPtr baseKlass, Efl.Object parent, [CallerFilePath] string file = null, [CallerLineNumber] int line = 0) { generated = Efl.Eo.BindingEntity.IsBindingEntity(((object)this).GetType()); IntPtr actual_klass = baseKlass; if (!generated) { actual_klass = Efl.Eo.ClassRegister.GetInheritKlassOrRegister(baseKlass, ((object)this).GetType()); } // Creation of the unfinalized Eo handle Eina.Log.Debug($"Instantiating from klass 0x{actual_klass.ToInt64():x}"); System.IntPtr parent_ptr = System.IntPtr.Zero; if (parent != null) { parent_ptr = parent.NativeHandle; } if (generated) { handle = Efl.Eo.Globals._efl_add_internal_start(file, line, actual_klass, parent_ptr, 1, 0); } else { handle = Efl.Eo.Globals._efl_add_internal_start_bindings(file, line, actual_klass, parent_ptr, 1, 0, Efl.Eo.Globals.efl_mono_avoid_top_level_constructor_callback_addr_get(), IntPtr.Zero); } if (handle == System.IntPtr.Zero) { throw new Exception("Instantiation failed"); } Eina.Log.Debug($"Eo instance right after internal_start 0x{handle.ToInt64():x} with refcount {Efl.Eo.Globals.efl_ref_count(handle)}"); Eina.Log.Debug($"Parent was 0x{parent_ptr.ToInt64()}"); // Creation of wrapper supervisor AddWrapperSupervisor(); } /// Destructor. ~EoWrapper() { Dispose(false); } /// Pointer to the native instance. public System.IntPtr NativeHandle { get { return handle; } } /// Pointer to the native class description. public abstract System.IntPtr NativeClass { get; } /// /// Whether this object type is one of the generated binding classes or a custom /// class defined by the user and that inherit from one of the generated ones. /// /// /// True if this object type is one of the generated binding classes, /// false if it is class that is manually defined and that inherits from /// one of the generated ones, including user defined classes. /// protected bool IsGeneratedBindingClass { get { return generated; } } /// Releases the underlying native instance. protected virtual void Dispose(bool disposing) { if (disposing && handle != System.IntPtr.Zero) { IntPtr h = handle; handle = IntPtr.Zero; Efl.Eo.Globals.efl_mono_native_dispose(h); } else { Monitor.Enter(Efl.All.InitLock); if (Efl.All.MainLoopInitialized) { Efl.Eo.Globals.efl_mono_thread_safe_native_dispose(handle); } Monitor.Exit(Efl.All.InitLock); } } /// Turns the native pointer into a string representation. /// A string with the type and the native pointer for this object. public override String ToString() { return $"{this.GetType().Name}@[0x{(UInt64)handle:x}]"; } /// Releases the underlying native instance. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// Releases the underlying Eo object. /// /// This method is a C# counterpart to the C `efl_del` function. It removes the parent of the object /// and releases the Eo reference it was holding. /// public void Del() { // FIXME Implement this ((Efl.Object)this).SetParent(null); Dispose(); } /// Finishes instantiating this object. /// Internal usage by generated code. protected void FinishInstantiation() { Eina.Log.Debug("calling efl_add_internal_end"); var h = Efl.Eo.Globals._efl_add_end(handle, 1, 0); Eina.Log.Debug($"efl_add_end returned eo 0x{handle.ToInt64():x}"); // if (h == IntPtr.Zero) // TODO // { // } handle = h; } /// Adds a new event handler, registering it to the native event. For internal use only. /// The name of the native library definining the event. /// The name of the native event. /// Delegate to be called by native code on event raising. /// Managed delegate that will be called by evtCaller on event raising. protected void AddNativeEventHandler(string lib, string key, Efl.EventCb evtCaller, object evtDelegate) { lock (eflBindingEventLock) { IntPtr desc = Efl.EventDescription.GetNative(lib, key); if (desc == IntPtr.Zero) { Eina.Log.Error($"Failed to get native event {key}"); return; } var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(handle); var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(wsPtr); if (ws.EoEvents.ContainsKey((desc, evtDelegate))) { Eina.Log.Warning($"Event proxy for event {key} already registered!"); return; } IntPtr evtCallerPtr = Marshal.GetFunctionPointerForDelegate(evtCaller); if (!Efl.Eo.Globals.efl_event_callback_priority_add(handle, desc, 0, evtCallerPtr, wsPtr)) { Eina.Log.Error($"Failed to add event proxy for event {key}"); return; } ws.EoEvents[(desc, evtDelegate)] = (evtCallerPtr, evtCaller); Eina.Error.RaiseIfUnhandledException(); } } /// Removes the given event handler for the given event. For internal use only. /// The name of the native library definining the event. /// The name of the native event. /// The delegate to be removed. protected void RemoveNativeEventHandler(string lib, string key, object evtDelegate) { lock (eflBindingEventLock) { IntPtr desc = Efl.EventDescription.GetNative(lib, key); if (desc == IntPtr.Zero) { Eina.Log.Error($"Failed to get native event {key}"); return; } var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(handle); var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(wsPtr); var evtPair = (desc, evtDelegate); if (ws.EoEvents.TryGetValue(evtPair, out var caller)) { if (!Efl.Eo.Globals.efl_event_callback_del(handle, desc, caller.evtCallerPtr, wsPtr)) { Eina.Log.Error($"Failed to remove event proxy for event {key}"); return; } ws.EoEvents.Remove(evtPair); Eina.Error.RaiseIfUnhandledException(); } else { Eina.Log.Error($"Trying to remove proxy for event {key} when it is not registered."); } } } private static void OwnershipUniqueCallback(IntPtr data, ref Efl.Event.NativeStruct evt) { var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(data); ws.MakeUnique(); } private static void OwnershipSharedCallback(IntPtr data, ref Efl.Event.NativeStruct evt) { var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(data); ws.MakeShared(); } /// Create and set to the internal native state a C# supervisor for this Eo wrapper. For internal use only. private void AddWrapperSupervisor() { var ws = new Efl.Eo.WrapperSupervisor(this); Efl.Eo.Globals.SetWrapperSupervisor(handle, ws); if (Efl.Eo.Globals.efl_ref_count(handle) > 1) { ws.MakeShared(); } AddOwnershipEventHandlers(); } /// Register handlers to ownership events, in order to control the object lifetime. For internal use only. private void AddOwnershipEventHandlers() { AddNativeEventHandler("eo", "_EFL_EVENT_INVALIDATE", ownershipUniqueDelegate, ownershipUniqueDelegate); AddNativeEventHandler("eo", "_EFL_EVENT_OWNERSHIP_UNIQUE", ownershipUniqueDelegate, ownershipUniqueDelegate); AddNativeEventHandler("eo", "_EFL_EVENT_OWNERSHIP_SHARED", ownershipSharedDelegate, ownershipSharedDelegate); Eina.Error.RaiseIfUnhandledException(); } /// /// Struct to be used when constructing objects from native code. /// Wraps the pointer handle to the native object instance. /// protected struct ConstructingHandle { /// Constructor for wrapping the native handle. public ConstructingHandle(IntPtr h) { NativeHandle = h; } /// Pointer to the native instance. public IntPtr NativeHandle { get; private set; } } /// /// Set a value object associated with a key object. /// public void SetKeyValue(object key, object val) { if (keyValueHash == null) keyValueHash = new Hashtable(); keyValueHash.Add(key, val); } /// /// Get a value object associated with a key object. /// public object GetKeyValue(object key) { if (keyValueHash == null) return null; return keyValueHash[key]; } /// Wrapper for native methods and virtual method delegates. /// For internal use by generated code only. public abstract class NativeMethods : Efl.Eo.NativeClass { private static EflConstructorDelegate csharpEflConstructorStaticDelegate = new EflConstructorDelegate(Constructor); private static Efl.Eo.NativeModule EoModule = new Efl.Eo.NativeModule("eo"); private delegate IntPtr EflConstructorDelegate(IntPtr obj, IntPtr pd); /// Gets the list of Eo operations to override. /// The list of Eo operations to be overload. public override System.Collections.Generic.List GetEoOps(Type type, bool includeInherited) { var descs = new System.Collections.Generic.List(); descs.Add(new EflOpDescription() { api_func = Efl.Eo.FunctionInterop.LoadFunctionPointer(EoModule.Module, "efl_constructor"), func = Marshal.GetFunctionPointerForDelegate(csharpEflConstructorStaticDelegate) }); return descs; } private static IntPtr Constructor(IntPtr obj, IntPtr pd) { try { var eoKlass = Efl.Eo.Globals.efl_class_get(obj); var managedType = ClassRegister.GetManagedType(eoKlass); if (managedType == null) { IntPtr nativeName = Efl.Eo.Globals.efl_class_name_get(eoKlass); var name = Eina.StringConversion.NativeUtf8ToManagedString(nativeName); Eina.Log.Warning($"Can't get Managed class for object handle 0x{(UInt64)obj:x} with native class [{name}]"); return IntPtr.Zero; } var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; ConstructorInfo constructor = managedType.GetConstructor(flags, null, new Type[1] { typeof(ConstructingHandle) }, null); if (constructor == null) { Eina.Log.Error($"Type {managedType.FullName} lacks a constructor that receives a ConstructingHandle. It can not be constructed from native code."); return IntPtr.Zero; } var eoWrapper = (Efl.Eo.IWrapper) constructor.Invoke(new object[1] { new ConstructingHandle(obj) }); if (eoWrapper == null) { Eina.Log.Warning("Constructor was unable to create a new object"); return IntPtr.Zero; } return eoWrapper.NativeHandle; } catch (Exception e) { Eina.Log.Warning($"Inherited constructor error: {e.ToString()}"); Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION); return IntPtr.Zero; } } } } } // namespace Eo /// Concrete realization of Efl.Object. /// /// Some legacy classes (like Evas.Canvas) may be returned by some methods. As these classes are not bound, we /// allow minimal interaction with them through . /// /// But as is abstract, whis realized class will allow us to create C# instances of it. internal class ObjectRealized : Efl.Object { protected ObjectRealized(Efl.Eo.Globals.WrappingHandle ch) : base(ch) { } } } // namespace Efl