diff --git a/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs b/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs index 373efac44..cf25a7aa3 100644 --- a/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs +++ b/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs @@ -8,6 +8,8 @@ public class BoxedWrapper { public static object WrapHandle(IntPtr handle, bool ownsHandle, Type gtype) { + return InstanceFactory.Create(handle, ownsHandle); + System.Type trueType = TypeDictionary.GetSystemType(gtype); if (handle == IntPtr.Zero) diff --git a/src/Libs/GObject-2.0/Internal/DemoTypeRegistration.cs b/src/Libs/GObject-2.0/Internal/DemoTypeRegistration.cs new file mode 100644 index 000000000..8c992e602 --- /dev/null +++ b/src/Libs/GObject-2.0/Internal/DemoTypeRegistration.cs @@ -0,0 +1,27 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; + +namespace GObject.Internal; + +internal class DemoTypeRegistration +{ + internal static void RegisterTypes() + { + Register(Binding.GetGType, (ptr, ownedRef) => new GObject.Binding(ptr, ownedRef), OSPlatform.Linux, OSPlatform.OSX, OSPlatform.Windows); + } + + private static void Register(Func getType, Func factory, params OSPlatform[] supportedPlatforms) where T : class + { + try + { + if (supportedPlatforms.Any(RuntimeInformation.IsOSPlatform)) + InstanceFactory.AddFactoryForType(getType(), factory); + } + catch (System.Exception e) + { + Debug.WriteLine($"Could not register type '{nameof(T)}': {e.Message}"); + } + } +} diff --git a/src/Libs/GObject-2.0/Internal/InstanceFactory.cs b/src/Libs/GObject-2.0/Internal/InstanceFactory.cs new file mode 100644 index 000000000..6d0953560 --- /dev/null +++ b/src/Libs/GObject-2.0/Internal/InstanceFactory.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace GObject.Internal; + +internal class InstanceFactory +{ + private static readonly Dictionary> Factories = new(); + + public static object Create(IntPtr handle, bool ownedRef) + { + var gtype = GetTypeFromInstance(handle); + + Debug.Assert( + condition: Functions.TypeName(gtype.Value).ConvertToString() == Functions.TypeNameFromInstance(new TypeInstanceUnownedHandle(handle)).ConvertToString(), + message: "GType name of instance and class do not match" + ); + + if (!Factories.TryGetValue(gtype, out var handleFactory)) + throw new Exception("Cant create handle for unknown type"); + + return handleFactory(handle, ownedRef); + } + + public static void AddFactoryForType(Type type, Func factory) + { + Factories[type] = factory; + } + + private static unsafe Type GetTypeFromInstance(IntPtr handle) + { + var klassPtr = Unsafe.AsRef((void*) handle).GClass; + var typeId = Unsafe.AsRef((void*) klassPtr).GType; + + + TypeInstanceData instance = Marshal.PtrToStructure(handle); + TypeClassData klass = Marshal.PtrToStructure(instance.GClass); + var typeid = klass.GType; + + if (typeid == 0) + throw new Exception("Could not retrieve type from class struct - is the struct valid?"); + + return new Type(typeid); + } +} diff --git a/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs b/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs index 796de78c7..119cd4c73 100644 --- a/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs +++ b/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs @@ -8,43 +8,22 @@ namespace GObject.Internal; public static class ObjectWrapper { - public static T? WrapNullableHandle(IntPtr handle, bool ownedRef) where T : class, IHandle + public static T? WrapNullableHandle(IntPtr handle, bool ownedRef) where T : GObject.Object, IHandle { return handle == IntPtr.Zero ? null : WrapHandle(handle, ownedRef); } - public static T WrapHandle(IntPtr handle, bool ownedRef) where T : class, IHandle + public static T WrapHandle(IntPtr handle, bool ownedRef) where T : GObject.Object, IHandle { - Debug.Assert( - condition: typeof(T).IsClass && typeof(T).IsAssignableTo(typeof(GObject.Object)), - message: "Type 'T' must be a GObject-based class" - ); - if (handle == IntPtr.Zero) throw new NullReferenceException($"Failed to wrap handle as type <{typeof(T).FullName}>. Null handle passed to WrapHandle."); if (ObjectMapper.TryGetObject(handle, out T? obj)) return obj; - //In case of classes prefer the type reported by the gobject type system over - //the expected type as often an API returns a less derived class in it's public - //API then the actual one. - Type gtype = GetTypeFromInstance(handle); - - Debug.Assert( - condition: Functions.TypeName(gtype.Value).ConvertToString() == Functions.TypeNameFromInstance(new TypeInstanceUnownedHandle(handle)).ConvertToString(), - message: "GType name of instance and class do not match" - ); - - System.Type trueType = TypeDictionary.GetSystemType(gtype); - ConstructorInfo? ctor = GetObjectConstructor(trueType); - - if (ctor == null) - throw new Exception($"Type {typeof(T).FullName} does not define an IntPtr constructor. This could mean improperly defined bindings"); - - return (T) ctor.Invoke(new object[] { handle, ownedRef }); + return (T) InstanceFactory.Create(handle, ownedRef); } public static T? WrapNullableInterfaceHandle(IntPtr handle, bool ownedRef) where T : class, IHandle @@ -78,18 +57,6 @@ public static T WrapInterfaceHandle(IntPtr handle, bool ownedRef) where T : c return (T) ctor.Invoke(new object[] { handle, ownedRef }); } - private static Type GetTypeFromInstance(IntPtr handle) - { - TypeInstanceData instance = Marshal.PtrToStructure(handle); - TypeClassData klass = Marshal.PtrToStructure(instance.GClass); - var typeid = klass.GType; - - if (typeid == 0) - throw new Exception("Could not retrieve type from class struct - is the struct valid?"); - - return new Type(typeid); - } - private static ConstructorInfo? GetObjectConstructor(System.Type type) { // Create using 'IntPtr' constructor