forked from enlightenment/efl
449 lines
17 KiB
C#
449 lines
17 KiB
C#
/*
|
|
* 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
|
|
{
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public abstract class EoWrapper : IWrapper, IDisposable
|
|
{
|
|
/// <summary>Object used to synchronize access to EFL events.</summary>
|
|
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;
|
|
|
|
/// <summary>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 <see cref="NativeHandle"/> 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.</summary>
|
|
/// <param name="ch">Tag struct storing the native handle of the object being constructed.</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="Object"/> 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.</summary>
|
|
/// <param name="wh">The native pointer to be wrapped.</param>
|
|
protected EoWrapper(Efl.Eo.Globals.WrappingHandle wh)
|
|
{
|
|
handle = wh.NativeHandle;
|
|
AddWrapperSupervisor();
|
|
}
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="Object"/> class.
|
|
/// Internal usage: Constructor to actually call the native library constructors. C# subclasses
|
|
/// must use the public constructor only.</summary>
|
|
/// <param name="baseKlass">The pointer to the base native Eo class.</param>
|
|
/// <param name="parent">The Efl.Object parent of this instance.</param>
|
|
/// <param name="file">Name of the file from where the constructor is called.</param>
|
|
/// <param name="line">Number of the line from where the constructor is called.</param>
|
|
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();
|
|
}
|
|
|
|
/// <summary>Destructor.</summary>
|
|
~EoWrapper()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
/// <summary>Pointer to the native instance.</summary>
|
|
public System.IntPtr NativeHandle
|
|
{
|
|
get { return handle; }
|
|
}
|
|
|
|
/// <summary>Pointer to the native class description.</summary>
|
|
public abstract System.IntPtr NativeClass
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// 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.
|
|
/// </returns>
|
|
protected bool IsGeneratedBindingClass
|
|
{
|
|
get { return generated; }
|
|
}
|
|
|
|
/// <summary>Releases the underlying native instance.</summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>Turns the native pointer into a string representation.</summary>
|
|
/// <returns>A string with the type and the native pointer for this object.</returns>
|
|
public override String ToString()
|
|
{
|
|
return $"{this.GetType().Name}@[0x{(UInt64)handle:x}]";
|
|
}
|
|
|
|
/// <summary>Releases the underlying native instance.</summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
/// <summary>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.
|
|
/// </summary>
|
|
public void Del()
|
|
{
|
|
// FIXME Implement this
|
|
((Efl.Object)this).SetParent(null);
|
|
Dispose();
|
|
}
|
|
|
|
/// <summary>Finishes instantiating this object.
|
|
/// Internal usage by generated code.</summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Adds a new event handler, registering it to the native event. For internal use only.</summary>
|
|
/// <param name="lib">The name of the native library definining the event.</param>
|
|
/// <param name="key">The name of the native event.</param>
|
|
/// <param name="evtCaller">Delegate to be called by native code on event raising.</param>
|
|
/// <param name="evtDelegate">Managed delegate that will be called by evtCaller on event raising.</param>
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>Removes the given event handler for the given event. For internal use only.</summary>
|
|
/// <param name="lib">The name of the native library definining the event.</param>
|
|
/// <param name="key">The name of the native event.</param>
|
|
/// <param name="evtDelegate">The delegate to be removed.</param>
|
|
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();
|
|
}
|
|
|
|
/// <summary>Create and set to the internal native state a C# supervisor for this Eo wrapper. For internal use only.</summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>Register handlers to ownership events, in order to control the object lifetime. For internal use only.</summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Struct to be used when constructing objects from native code.
|
|
/// Wraps the pointer handle to the native object instance.
|
|
/// </summary>
|
|
protected struct ConstructingHandle
|
|
{
|
|
/// <summary>Constructor for wrapping the native handle.</summary>
|
|
public ConstructingHandle(IntPtr h)
|
|
{
|
|
NativeHandle = h;
|
|
}
|
|
|
|
/// <summary>Pointer to the native instance.</summary>
|
|
public IntPtr NativeHandle { get; private set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a value object associated with a key object.
|
|
/// </summary>
|
|
public void SetKeyValue(object key, object val)
|
|
{
|
|
if (keyValueHash == null)
|
|
keyValueHash = new Hashtable();
|
|
|
|
keyValueHash.Add(key, val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a value object associated with a key object.
|
|
/// </summary>
|
|
public object GetKeyValue(object key)
|
|
{
|
|
if (keyValueHash == null)
|
|
return null;
|
|
|
|
return keyValueHash[key];
|
|
}
|
|
|
|
/// <summary>Wrapper for native methods and virtual method delegates.
|
|
/// For internal use by generated code only.</summary>
|
|
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);
|
|
|
|
/// <summary>Gets the list of Eo operations to override.</summary>
|
|
/// <returns>The list of Eo operations to be overload.</returns>
|
|
public override System.Collections.Generic.List<EflOpDescription> GetEoOps(Type type, bool includeInherited)
|
|
{
|
|
var descs = new System.Collections.Generic.List<EflOpDescription>();
|
|
|
|
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
|
|
|
|
/// <summary>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 <see cref="Efl.Object" />.
|
|
///
|
|
/// But as <see cref="Efl.Object" /> is abstract, whis realized class will allow us to create C# instances of it.</summary>
|
|
internal class ObjectRealized : Efl.Object
|
|
{
|
|
protected ObjectRealized(Efl.Eo.Globals.WrappingHandle ch) : base(ch) { }
|
|
}
|
|
|
|
} // namespace Efl
|