From e07d6c824e2b22d442fbaca0c87c6f437b1cd239 Mon Sep 17 00:00:00 2001 From: badcel <1218031+badcel@users.noreply.github.com> Date: Tue, 31 Oct 2023 10:16:15 +0100 Subject: [PATCH] Support boxed records --- .../Generator/Internal/TypedRecord.cs | 29 ++ .../Generator/Internal/TypedRecordData.cs | 29 ++ .../Internal/TypedRecordDelegates.cs | 33 ++ .../Generator/Internal/TypedRecordHandle.cs | 29 ++ .../Generator/Generator/Public/TypedRecord.cs | 29 ++ src/Generation/Generator/Model/Parameter.cs | 3 + src/Generation/Generator/Model/Record.cs | 10 +- src/Generation/Generator/Model/TypedRecord.cs | 83 +++++ src/Generation/Generator/Records.cs | 7 + .../CallbackCommonHandlerRenderUtils.cs | 15 + .../Field/Converter/OpaqueTypedRecord.cs | 18 + .../Field/Converter/OpaqueUntypedRecord.cs | 18 + .../Internal/Field/Converter/Record.cs | 2 +- .../Internal/Field/Converter/RecordArray.cs | 2 +- .../Internal/Field/Converter/TypedRecord.cs | 28 ++ .../Field/Converter/TypedRecordArray.cs | 33 ++ .../Renderer/Internal/Field/Fields.cs | 17 +- .../Converter/TypedRecord.cs | 28 ++ .../InstanceParameter/InstanceParameters.cs | 1 + .../Internal/Parameter/CallbackParameters.cs | 3 + .../Parameter/Converter/TypedRecord.cs | 41 +++ .../Parameter/Converter/TypedRecordArray.cs | 41 +++ .../Converter/TypedRecordCallback.cs | 30 ++ .../Converter/TypedRecordCallbackAlias.cs | 30 ++ .../Converter/TypedRecordCallbackArray.cs | 39 ++ .../Renderer/Internal/Parameter/Parameters.cs | 2 + .../Converter/TypedRecord.cs | 87 +++++ .../Converter/TypedRecordArray.cs | 76 ++++ .../ParameterToManagedExpression.cs | 2 + .../ReturnType/Converter/TypedRecord.cs | 25 ++ .../ReturnType/Converter/TypedRecordArray.cs | 17 + .../Converter/TypedRecordCallback.cs | 14 + .../Internal/ReturnType/ReturnTypeRenderer.cs | 2 + .../ReturnType/ReturnTypeRendererCallback.cs | 2 + .../Converter/TypedRecord.cs | 21 ++ .../ReturnTypeToNativeExpression.cs | 1 + .../Renderer/Internal/TypedRecord.cs | 30 ++ .../Renderer/Internal/TypedRecordData.cs | 27 ++ .../Renderer/Internal/TypedRecordDelegates.cs | 31 ++ .../Renderer/Internal/TypedRecordHandle.cs | 275 ++++++++++++++ .../Public/Callback/CallbackDelegate.cs | 1 - .../Class/Signals/ClassSignal.Parameter.cs | 2 +- .../Public/Constructor/ConstructorRenderer.cs | 9 +- .../Constructor/Converter/TypedRecord.cs | 32 ++ .../Converter/TypedRecord.cs | 21 ++ .../InstanceParameterToNativeExpression.cs | 1 + .../Public/Interface/InterfaceMethods.cs | 6 + .../Public/Parameter/Converter/Pointer.cs | 1 - .../Public/Parameter/Converter/TypedRecord.cs | 32 ++ .../Parameter/Converter/TypedRecordArray.cs | 31 ++ .../Public/Parameter/ParameterRenderer.cs | 2 + .../Converter/Pointer.cs | 3 +- .../Converter/TypedRecord.cs | 50 +++ .../Converter/TypedRecordArray.cs | 114 ++++++ .../Converter/Utf8String.cs | 74 ++-- .../ParameterToNativeExpression.cs | 2 + .../ReturnType/Converter/TypedRecord.cs | 16 + .../ReturnType/Converter/TypedRecordArray.cs | 14 + .../Public/ReturnType/ReturnTypeRenderer.cs | 3 + .../ReturnType/ReturnTypeRendererCallback.cs | 4 +- .../Converter/TypedRecord.cs | 28 ++ .../ReturnTypeToManagedExpression.cs | 1 + .../Generator/Renderer/Public/TypedRecord.cs | 62 ++++ src/Generation/GirLoader/Output/Class.cs | 2 +- src/Generation/GirLoader/Output/Interface.cs | 3 + src/Generation/GirLoader/Output/Record.cs | 3 + src/GirCore.Libraries.props | 1 + src/Libs/GLib-2.0/Public/Source.cs | 19 - src/Libs/GObject-2.0/GObject-2.0.csproj | 5 +- src/Libs/GObject-2.0/Public/Closure.cs | 33 +- .../GObject-2.0/Public/ConstructArgument.cs | 26 +- src/Libs/GObject-2.0/Public/Object.cs | 15 +- src/Libs/GObject-2.0/Public/SignalArgs.cs | 2 + src/Libs/GObject-2.0/Public/Value.cs | 231 +++++------- src/Libs/Gst-1.0/Public/Bus.cs | 21 -- src/Libs/Gst-1.0/Public/Element.cs | 176 --------- src/Libs/Gst-1.0/Public/Global.cs | 18 - src/Libs/Gst-1.0/Public/Message.cs | 129 ------- src/Libs/Gst-1.0/Public/Object.cs | 48 --- src/Libs/Gst-1.0/Public/Pad.cs | 53 --- src/Libs/Gst-1.0/Public/Structure.cs | 39 -- src/Libs/Gst-1.0/Public/TagList.cs | 75 ---- src/Libs/Gst-1.0/Public/Version.cs | 12 - .../cairo-1.0/Internal/ContextOwnedHandle.cs | 10 - .../cairo-1.0/Internal/DeviceOwnedHandle.cs | 10 - .../cairo-1.0/Internal/FontFaceOwnedHandle.cs | 10 - .../Internal/FontOptionsOwnedHandle.cs | 10 - .../cairo-1.0/Internal/PatternOwnedHandle.cs | 10 - .../cairo-1.0/Internal/RegionOwnedHandle.cs | 10 - .../Internal/ScaledFontOwnedHandle.cs | 10 - .../cairo-1.0/Internal/SurfaceOwnedHandle.cs | 10 - src/Libs/cairo-1.0/Public/Context.Text.cs | 4 +- src/Libs/cairo-1.0/Public/Context.cs | 6 +- src/Libs/cairo-1.0/Public/Gradient.cs | 2 +- src/Libs/cairo-1.0/Public/ScaledFont.cs | 2 +- src/Libs/cairo-1.0/Public/SolidPattern.cs | 2 +- src/Libs/cairo-1.0/Public/Surface.cs | 11 +- src/Libs/cairo-1.0/Public/SurfacePattern.cs | 2 +- src/Native/GirTestLib/girtest-record-tester.c | 52 --- src/Native/GirTestLib/girtest-record-tester.h | 25 -- .../GirTestLib/girtest-typed-record-tester.c | 346 ++++++++++++++++++ .../GirTestLib/girtest-typed-record-tester.h | 103 ++++++ src/Native/GirTestLib/girtest.h | 2 +- src/Native/GirTestLib/meson.build | 4 +- src/Samples/Gst-1.0/VideoPlayback/Play.cs | 2 +- .../Libs/Cairo-1.0.Tests/ImageSurfaceTest.cs | 2 - .../GObject-2.0.Tests/Records/ValueTest.cs | 104 ++++-- .../Libs/GirTest-0.1.Tests/BitfieldTest.cs | 12 +- .../Libs/GirTest-0.1.Tests/EnumerationTest.cs | 2 + .../Libs/GirTest-0.1.Tests/TypedRecordTest.cs | 320 ++++++++++++++++ 110 files changed, 2666 insertions(+), 1047 deletions(-) create mode 100644 src/Generation/Generator/Generator/Internal/TypedRecord.cs create mode 100644 src/Generation/Generator/Generator/Internal/TypedRecordData.cs create mode 100644 src/Generation/Generator/Generator/Internal/TypedRecordDelegates.cs create mode 100644 src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs create mode 100644 src/Generation/Generator/Generator/Public/TypedRecord.cs create mode 100644 src/Generation/Generator/Model/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueTypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueUntypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecordArray.cs create mode 100644 src/Generation/Generator/Renderer/Internal/InstanceParameter/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordArray.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallback.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackAlias.cs create mode 100644 src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackArray.cs create mode 100644 src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecordArray.cs create mode 100644 src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordArray.cs create mode 100644 src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordCallback.cs create mode 100644 src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Internal/TypedRecordData.cs create mode 100644 src/Generation/Generator/Renderer/Internal/TypedRecordDelegates.cs create mode 100644 src/Generation/Generator/Renderer/Internal/TypedRecordHandle.cs create mode 100644 src/Generation/Generator/Renderer/Public/Constructor/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecordArray.cs create mode 100644 src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecordArray.cs create mode 100644 src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecordArray.cs create mode 100644 src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/TypedRecord.cs create mode 100644 src/Generation/Generator/Renderer/Public/TypedRecord.cs delete mode 100644 src/Libs/GLib-2.0/Public/Source.cs delete mode 100644 src/Libs/Gst-1.0/Public/Bus.cs delete mode 100644 src/Libs/Gst-1.0/Public/Element.cs delete mode 100644 src/Libs/Gst-1.0/Public/Global.cs delete mode 100644 src/Libs/Gst-1.0/Public/Message.cs delete mode 100644 src/Libs/Gst-1.0/Public/Object.cs delete mode 100644 src/Libs/Gst-1.0/Public/Pad.cs delete mode 100644 src/Libs/Gst-1.0/Public/Structure.cs delete mode 100644 src/Libs/Gst-1.0/Public/TagList.cs delete mode 100644 src/Libs/Gst-1.0/Public/Version.cs delete mode 100644 src/Libs/cairo-1.0/Internal/ContextOwnedHandle.cs delete mode 100644 src/Libs/cairo-1.0/Internal/DeviceOwnedHandle.cs delete mode 100644 src/Libs/cairo-1.0/Internal/FontFaceOwnedHandle.cs delete mode 100644 src/Libs/cairo-1.0/Internal/FontOptionsOwnedHandle.cs delete mode 100644 src/Libs/cairo-1.0/Internal/PatternOwnedHandle.cs delete mode 100644 src/Libs/cairo-1.0/Internal/RegionOwnedHandle.cs delete mode 100644 src/Libs/cairo-1.0/Internal/ScaledFontOwnedHandle.cs delete mode 100644 src/Libs/cairo-1.0/Internal/SurfaceOwnedHandle.cs delete mode 100644 src/Native/GirTestLib/girtest-record-tester.c delete mode 100644 src/Native/GirTestLib/girtest-record-tester.h create mode 100644 src/Native/GirTestLib/girtest-typed-record-tester.c create mode 100644 src/Native/GirTestLib/girtest-typed-record-tester.h create mode 100644 src/Tests/Libs/GirTest-0.1.Tests/TypedRecordTest.cs diff --git a/src/Generation/Generator/Generator/Internal/TypedRecord.cs b/src/Generation/Generator/Generator/Internal/TypedRecord.cs new file mode 100644 index 000000000..2176dc1f0 --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecord.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecord : Generator +{ + private readonly Publisher _publisher; + + public TypedRecord(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record obj) + { + if (!Record.IsTyped(obj)) + return; + + var source = Renderer.Internal.TypedRecord.Render(obj); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(obj.Namespace), + Name: obj.Name, + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Internal/TypedRecordData.cs b/src/Generation/Generator/Generator/Internal/TypedRecordData.cs new file mode 100644 index 000000000..77f95a5ef --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecordData.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecordData : Generator +{ + private readonly Publisher _publisher; + + public TypedRecordData(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record obj) + { + if (!Record.IsTyped(obj)) + return; + + var source = Renderer.Internal.TypedRecordData.Render(obj); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(obj.Namespace), + Name: Model.TypedRecord.GetDataName(obj), + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Internal/TypedRecordDelegates.cs b/src/Generation/Generator/Generator/Internal/TypedRecordDelegates.cs new file mode 100644 index 000000000..9e1414b63 --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecordDelegates.cs @@ -0,0 +1,33 @@ +using System.Linq; +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecordDelegates : Generator +{ + private readonly Publisher _publisher; + + public TypedRecordDelegates(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record record) + { + if (!Record.IsTyped(record)) + return; + + if (!record.Fields.Any(field => field.AnyTypeOrCallback.IsT1)) + return; + + var source = Renderer.Internal.TypedRecordDelegates.Render(record); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(record.Namespace), + Name: $"{Model.TypedRecord.GetDataName(record)}.Delegates", + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs b/src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs new file mode 100644 index 000000000..313e53964 --- /dev/null +++ b/src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Internal; + +internal class TypedRecordHandle : Generator +{ + private readonly Publisher _publisher; + + public TypedRecordHandle(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record obj) + { + if (!Record.IsTyped(obj)) + return; + + var source = Renderer.Internal.TypedRecordHandle.Render(obj); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(obj.Namespace), + Name: Model.TypedRecord.GetInternalHandle(obj), + Source: source, + IsInternal: true + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Generator/Public/TypedRecord.cs b/src/Generation/Generator/Generator/Public/TypedRecord.cs new file mode 100644 index 000000000..d8880ac1c --- /dev/null +++ b/src/Generation/Generator/Generator/Public/TypedRecord.cs @@ -0,0 +1,29 @@ +using Generator.Model; + +namespace Generator.Generator.Public; + +internal class TypedRecord : Generator +{ + private readonly Publisher _publisher; + + public TypedRecord(Publisher publisher) + { + _publisher = publisher; + } + + public void Generate(GirModel.Record record) + { + if (!Record.IsTyped(record)) + return; + + var source = Renderer.Public.TypedRecord.Render(record); + var codeUnit = new CodeUnit( + Project: Namespace.GetCanonicalName(record.Namespace), + Name: Record.GetPublicClassName(record), + Source: source, + IsInternal: false + ); + + _publisher.Publish(codeUnit); + } +} diff --git a/src/Generation/Generator/Model/Parameter.cs b/src/Generation/Generator/Model/Parameter.cs index fa1f49989..c629d011a 100644 --- a/src/Generation/Generator/Model/Parameter.cs +++ b/src/Generation/Generator/Model/Parameter.cs @@ -20,6 +20,9 @@ public static bool IsGLibError(GirModel.Parameter parameter) if (!parameter.IsPointer) return false; + if (parameter.Name != "error") + return false; + if (parameter.AnyTypeOrVarArgs.TryPickT0(out var anyType, out _) && anyType.TryPickT0(out var type, out _) && type is GirModel.Record record) diff --git a/src/Generation/Generator/Model/Record.cs b/src/Generation/Generator/Model/Record.cs index 86bf13f6a..323c9f5e4 100644 --- a/src/Generation/Generator/Model/Record.cs +++ b/src/Generation/Generator/Model/Record.cs @@ -4,7 +4,7 @@ internal static partial class Record { public static bool IsStandard(GirModel.Record record) { - return !IsOpaqueTyped(record) && !IsOpaqueUntyped(record); + return !IsOpaqueTyped(record) && !IsOpaqueUntyped(record) && !IsTyped(record); } public static bool IsOpaqueTyped(GirModel.Record record) @@ -22,6 +22,14 @@ public static bool IsOpaqueUntyped(GirModel.Record record) return record is { Opaque: true, TypeFunction: null or { CIdentifier: "intern" } }; } + public static bool IsTyped(GirModel.Record record) + { + //Even if there is a TypeFunction it does not mean that it actually is + //a typed / boxed record. There is a magic keyword "intern" which means this + //record is actually fundamental and does not have a type function. + return record is { Opaque: false, TypeFunction.CIdentifier: not "intern" }; + } + public static string GetFullyQualifiedInternalStructName(GirModel.Record record) => Namespace.GetInternalName(record.Namespace) + "." + GetInternalStructName(record); diff --git a/src/Generation/Generator/Model/TypedRecord.cs b/src/Generation/Generator/Model/TypedRecord.cs new file mode 100644 index 000000000..08c99d4c6 --- /dev/null +++ b/src/Generation/Generator/Model/TypedRecord.cs @@ -0,0 +1,83 @@ +using System.Linq; + +namespace Generator.Model; + +internal static class TypedRecord +{ + public static string GetPublicClassName(GirModel.Record record) + => record.Name; + + public static string GetFullyQualifiedPublicClassName(GirModel.Record record) + => Namespace.GetPublicName(record.Namespace) + "." + GetPublicClassName(record); + + public static string GetFullyQualifiedInternalClassName(GirModel.Record record) + => Namespace.GetInternalName(record.Namespace) + "." + record.Name; + + public static string GetInternalHandle(GirModel.Record record) + => $"{Type.GetName(record)}Handle"; + + public static string GetInternalManagedHandle(GirModel.Record record) + => $"{Type.GetName(record)}ManagedHandle"; + + public static string GetInternalOwnedHandle(GirModel.Record record) + => $"{Type.GetName(record)}OwnedHandle"; + + public static string GetInternalUnownedHandle(GirModel.Record record) + => $"{Type.GetName(record)}UnownedHandle"; + + public static string GetFullyQuallifiedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalHandle(record)}"; + + public static string GetFullyQuallifiedOwnedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalOwnedHandle(record)}"; + + public static string GetFullyQuallifiedUnownedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalUnownedHandle(record)}"; + + public static string GetFullyQuallifiedNullHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalUnownedHandle(record)}.NullHandle"; + + public static string GetDataName(GirModel.Record record) + => $"{Type.GetName(record)}Data"; + + public static string GetFullyQuallifiedDataName(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetDataName(record)}"; + + public static string GetInternalArrayHandle(GirModel.Record record) + { + var prefix = $"{Type.GetName(record)}Array"; + if (record.Namespace.Records.Select(x => x.Name).Contains(prefix)) + prefix += "2"; + + return $"{prefix}Handle"; + } + + public static string GetFullyQuallifiedArrayHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayHandle(record)}"; + + public static string GetInternalArrayOwnedHandle(GirModel.Record record) + { + var prefix = $"{Type.GetName(record)}Array"; + if (record.Namespace.Records.Select(x => x.Name).Contains(prefix)) + prefix += "2"; + + return $"{prefix}OwnedHandle"; + } + + public static string GetFullyQuallifiedArrayOwnedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayOwnedHandle(record)}"; + + public static string GetInternalArrayUnownedHandle(GirModel.Record record) + { + var prefix = $"{Type.GetName(record)}Array"; + if (record.Namespace.Records.Select(x => x.Name).Contains(prefix)) + prefix += "2"; + return $"{prefix}UnownedHandle"; + } + + public static string GetFullyQuallifiedArrayUnownedHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayUnownedHandle(record)}"; + + public static string GetFullyQuallifiedArrayNullHandle(GirModel.Record record) + => $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalArrayUnownedHandle(record)}.NullHandle"; +} diff --git a/src/Generation/Generator/Records.cs b/src/Generation/Generator/Records.cs index b59f7f2c0..2285b9f22 100644 --- a/src/Generation/Generator/Records.cs +++ b/src/Generation/Generator/Records.cs @@ -22,6 +22,13 @@ public static void Generate(IEnumerable records, string path) new Generator.Internal.OpaqueUntypedRecordHandle(publisher), new Generator.Public.OpaqueUntypedRecord(publisher), + //Typed records + new Generator.Internal.TypedRecord(publisher), + new Generator.Internal.TypedRecordDelegates(publisher), + new Generator.Internal.TypedRecordHandle(publisher), + new Generator.Internal.TypedRecordData(publisher), + new Generator.Public.TypedRecord(publisher), + //Regular records new Generator.Internal.RecordDelegates(publisher), new Generator.Internal.RecordHandle(publisher), diff --git a/src/Generation/Generator/Renderer/Internal/Callback/CallbackCommonHandlerRenderUtils.cs b/src/Generation/Generator/Renderer/Internal/Callback/CallbackCommonHandlerRenderUtils.cs index 395659bad..b96fe2739 100644 --- a/src/Generation/Generator/Renderer/Internal/Callback/CallbackCommonHandlerRenderUtils.cs +++ b/src/Generation/Generator/Renderer/Internal/Callback/CallbackCommonHandlerRenderUtils.cs @@ -14,6 +14,7 @@ public static string RenderNativeCallback(GirModel.Callback callback, GirModel.S NativeCallback = ({GetParameterDefinition(parameterData)}{Error.RenderCallback(callback)}) => {{ {RenderConvertParameterStatements(parameterData)} {RenderCallStatement(callback, parameterData, out var resultVariableName)} + {RenderPostCallStatements(parameterData)} {RenderFreeStatement(scope)} {RenderReturnStatement(callback, resultVariableName)} }};"; @@ -100,6 +101,20 @@ private static string RenderCallStatement(GirModel.Callback callback, IEnumerabl return call.ToString(); } + private static string RenderPostCallStatements(IEnumerable data) + { + var call = new StringBuilder(); + + foreach (var p in data) + { + var postCallExpression = p.GetPostCallExpression(); + if (postCallExpression is not null) + call.AppendLine(postCallExpression); + } + + return call.ToString(); + } + private static string RenderFreeStatement(GirModel.Scope? scope) { return scope == GirModel.Scope.Async diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueTypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueTypedRecord.cs new file mode 100644 index 000000000..aeff8ac14 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueTypedRecord.cs @@ -0,0 +1,18 @@ +namespace Generator.Renderer.Internal.Field; + +internal class OpaqueTypedRecord : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsOpaqueTyped(record); + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: null, + NullableTypeName: Model.Type.Pointer + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueUntypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueUntypedRecord.cs new file mode 100644 index 000000000..f94c9de5f --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/OpaqueUntypedRecord.cs @@ -0,0 +1,18 @@ +namespace Generator.Renderer.Internal.Field; + +internal class OpaqueUntypedRecord : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsOpaqueUntyped(record); + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: null, + NullableTypeName: Model.Type.Pointer + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs index 00f5feb77..d15d5e7a3 100644 --- a/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/Record.cs @@ -6,7 +6,7 @@ internal class Record : FieldConverter { public bool Supports(GirModel.Field field) { - return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(); + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsStandard(record); } public RenderableField Convert(GirModel.Field field) diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs index 190fefe61..a4b40f5e8 100644 --- a/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/RecordArray.cs @@ -4,7 +4,7 @@ internal class RecordArray : FieldConverter { public bool Supports(GirModel.Field field) { - return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.IsArray(); + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.IsArray(out var record) && Model.Record.IsStandard(record); ; } public RenderableField Convert(GirModel.Field field) diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecord.cs new file mode 100644 index 000000000..23de49944 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecord.cs @@ -0,0 +1,28 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal.Field; + +internal class TypedRecord : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: null, + NullableTypeName: GetNullableTypeName(field) + ); + } + + private static string GetNullableTypeName(GirModel.Field field) + { + var type = (GirModel.Record) field.AnyTypeOrCallback.AsT0.AsT0; + return field.IsPointer + ? Type.Pointer + : Model.Record.GetFullyQualifiedInternalStructName(type); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..70e606d12 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Field/Converter/TypedRecordArray.cs @@ -0,0 +1,33 @@ +namespace Generator.Renderer.Internal.Field; + +internal class TypedRecordArray : FieldConverter +{ + public bool Supports(GirModel.Field field) + { + return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.IsArray(out var record) && Model.Record.IsTyped(record); ; + } + + public RenderableField Convert(GirModel.Field field) + { + return new RenderableField( + Name: Model.Field.GetName(field), + Attribute: GetAttribute(field), + NullableTypeName: GetNullableTypeName(field) + ); + } + + private static string? GetAttribute(GirModel.Field field) + { + var arrayType = field.AnyTypeOrCallback.AsT0.AsT1; + return arrayType.FixedSize is not null + ? MarshalAs.UnmanagedByValArray(sizeConst: arrayType.FixedSize.Value) + : null; + } + + private static string GetNullableTypeName(GirModel.Field field) + { + var arrayType = field.AnyTypeOrCallback.AsT0.AsT1; + var type = (GirModel.Record) arrayType.AnyType.AsT0; + return Model.Record.GetFullyQualifiedInternalStructName(type) + "[]"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Field/Fields.cs b/src/Generation/Generator/Renderer/Internal/Field/Fields.cs index 2535b6ff2..91bcbaf8e 100644 --- a/src/Generation/Generator/Renderer/Internal/Field/Fields.cs +++ b/src/Generation/Generator/Renderer/Internal/Field/Fields.cs @@ -14,6 +14,8 @@ internal static class Fields new Field.ClassArray(), new Field.Enumeration(), new Field.EnumerationArray(), + new Field.OpaqueTypedRecord(), + new Field.OpaqueUntypedRecord(), new Field.Pointer(), new Field.PointerAlias(), new Field.PointerArray(), @@ -25,6 +27,8 @@ internal static class Fields new Field.RecordArray(), new Field.String(), new Field.StringArray(), + new Field.TypedRecord(), + new Field.TypedRecordArray(), new Field.Union(), new Field.UnionArray(), }; @@ -40,16 +44,17 @@ public static string Render(IEnumerable fields) } public static string Render(GirModel.Field field) + { + var renderableField = GetRenderableField(field); + return $"{renderableField.Attribute} public {renderableField.NullableTypeName} {renderableField.Name};"; + } + + public static Field.RenderableField GetRenderableField(GirModel.Field field) { foreach (var converter in converters) if (converter.Supports(field)) - return Render(converter.Convert(field)); + return converter.Convert(field); throw new System.Exception($"Internal field \"{field.Name}\" of type {field.AnyTypeOrCallback} can not be rendered"); } - - private static string Render(Field.RenderableField field) - { - return @$"{field.Attribute} public {field.NullableTypeName} {field.Name};"; - } } diff --git a/src/Generation/Generator/Renderer/Internal/InstanceParameter/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/InstanceParameter/Converter/TypedRecord.cs new file mode 100644 index 000000000..52c46a07c --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/InstanceParameter/Converter/TypedRecord.cs @@ -0,0 +1,28 @@ +namespace Generator.Renderer.Internal.InstanceParameter; + +internal class TypedRecord : InstanceParameterConverter +{ + public bool Supports(GirModel.Type type) + { + return type is GirModel.Record r && Model.Record.IsTyped(r); + } + + public RenderableInstanceParameter Convert(GirModel.InstanceParameter instanceParameter) + { + return new RenderableInstanceParameter( + Name: Model.InstanceParameter.GetName(instanceParameter), + NullableTypeName: GetNullableTypeName(instanceParameter) + ); + } + + private static string GetNullableTypeName(GirModel.InstanceParameter instanceParameter) + { + var type = (GirModel.Record) instanceParameter.Type; + return instanceParameter switch + { + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.None } => Model.TypedRecord.GetFullyQuallifiedHandle(type), + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type), + _ => throw new System.Exception($"Can't detect typed record instance parameter type {instanceParameter.Name}: CallerAllocates={instanceParameter.CallerAllocates} Direction={instanceParameter.Direction} Transfer={instanceParameter.Transfer}") + }; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs b/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs index a3907d6bf..6222183a4 100644 --- a/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs +++ b/src/Generation/Generator/Renderer/Internal/InstanceParameter/InstanceParameters.cs @@ -13,6 +13,7 @@ internal static class InstanceParameters new InstanceParameter.OpaqueUntypedRecord(), new InstanceParameter.Pointer(), new InstanceParameter.Record(), + new InstanceParameter.TypedRecord(), new InstanceParameter.Union() }; diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs b/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs index 305f8cef9..ea790cc3a 100644 --- a/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs +++ b/src/Generation/Generator/Renderer/Internal/Parameter/CallbackParameters.cs @@ -36,6 +36,9 @@ internal static class CallbackParameters new Parameter.RecordCallback(), //Callbacks do not support record safe handles in parameters new Parameter.RecordGLibPtrArray(), new Parameter.String(), + new Parameter.TypedRecordCallback(), + new Parameter.TypedRecordCallbackAlias(), + new Parameter.TypedRecordCallbackArray(), new Parameter.Union(), new Parameter.UnionArray(), new Parameter.UnsignedPointer(), diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecord.cs new file mode 100644 index 000000000..a82f4339a --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecord.cs @@ -0,0 +1,41 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecord : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: GetDirection(parameter), + NullableTypeName: GetNullableTypeName(parameter), + Name: Model.Parameter.GetName(parameter) + ); + } + + private static string GetNullableTypeName(GirModel.Parameter parameter) + { + //Native records are represented as SafeHandles and are not nullable + + var type = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT0; + return parameter switch + { + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.None } => Model.TypedRecord.GetFullyQuallifiedHandle(type), + { Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type), + _ => throw new Exception($"Can't detect record parameter type {parameter.Name}: CallerAllocates={parameter.CallerAllocates} Direction={parameter.Direction} Transfer={parameter.Transfer}") + }; + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + { Direction: GirModel.Direction.InOut } => ParameterDirection.In(), + _ => throw new Exception($"Unknown parameter direction for opaque typed record parameter {parameter.Name}") + }; +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..90ca39b9c --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordArray.cs @@ -0,0 +1,41 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecordArray : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.IsArray(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + if (parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + return PointerArray(parameter); + + return StructArray(parameter); + } + + private static RenderableParameter PointerArray(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: $"ref {Model.Type.Pointer}", + Name: Model.Parameter.GetName(parameter) + ); + } + + private static RenderableParameter StructArray(GirModel.Parameter parameter) + { + var record = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0; + + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: Model.TypedRecord.GetFullyQuallifiedArrayHandle(record), + Name: Model.Parameter.GetName(parameter) + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallback.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallback.cs new file mode 100644 index 000000000..ffd774957 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallback.cs @@ -0,0 +1,30 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecordCallback : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: GetDirection(parameter), + NullableTypeName: Model.Type.Pointer, + Name: Model.Parameter.GetName(parameter) + ); + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + { Direction: GirModel.Direction.InOut } => ParameterDirection.In(), + { Direction: GirModel.Direction.Out, CallerAllocates: true } => ParameterDirection.In(), + { Direction: GirModel.Direction.Out } => ParameterDirection.Out(), + _ => throw new Exception("Unknown direction for record parameter in callback") + }; +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackAlias.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackAlias.cs new file mode 100644 index 000000000..81e838ef1 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackAlias.cs @@ -0,0 +1,30 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecordCallbackAlias : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.IsAlias(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: GetDirection(parameter), + NullableTypeName: Model.Type.Pointer, + Name: Model.Parameter.GetName(parameter) + ); + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + { Direction: GirModel.Direction.InOut } => ParameterDirection.In(), + { Direction: GirModel.Direction.Out, CallerAllocates: true } => ParameterDirection.In(), + { Direction: GirModel.Direction.Out } => ParameterDirection.Out(), + _ => throw new Exception("Unknown direction for record parameter in callback") + }; +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackArray.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackArray.cs new file mode 100644 index 000000000..751aceb4f --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Converter/TypedRecordCallbackArray.cs @@ -0,0 +1,39 @@ +using System; + +namespace Generator.Renderer.Internal.Parameter; + +internal class TypedRecordCallbackArray : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.IsArray(out var record) && Model.Record.IsTyped(record); + } + + public RenderableParameter Convert(GirModel.Parameter parameter) + { + if (parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + return PointerArray(parameter); + + return StructArray(parameter); + } + + private static RenderableParameter PointerArray(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: $"ref {Model.Type.Pointer}", + Name: Model.Parameter.GetName(parameter) + ); + } + + private static RenderableParameter StructArray(GirModel.Parameter parameter) + { + return new RenderableParameter( + Attribute: string.Empty, + Direction: string.Empty, + NullableTypeName: Model.Type.Pointer, + Name: Model.Parameter.GetName(parameter) + ); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs b/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs index 31a552824..ea3d53030 100644 --- a/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs +++ b/src/Generation/Generator/Renderer/Internal/Parameter/Parameters.cs @@ -41,6 +41,8 @@ internal static class Parameters new Parameter.RecordGLibPtrArray(), new Parameter.String(), new Parameter.StringGLibPtrArray(), + new Parameter.TypedRecord(), + new Parameter.TypedRecordArray(), new Parameter.Union(), new Parameter.UnionArray(), new Parameter.UnsignedPointer(), diff --git a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..f476d7773 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecord.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using GirModel; + +namespace Generator.Renderer.Internal.ParameterToManagedExpressions; + +internal class TypedRecord : ToManagedParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToManagedData parameterData, IEnumerable parameters) + { + + if (Model.Parameter.IsGLibError(parameterData.Parameter)) + ErrorRecord(parameterData); + else + RegularRecord(parameterData); + } + + private static void ErrorRecord(ParameterToManagedData parameterData) + { + parameterData.IsGLibErrorParameter = true; + + var name = Model.Parameter.GetName(parameterData.Parameter); + parameterData.SetSignatureName(name); + parameterData.SetCallName(name); + } + + private static void RegularRecord(ParameterToManagedData parameterData) + { + switch (parameterData.Parameter.Direction) + { + case GirModel.Direction.In: + In(parameterData); + break; + case GirModel.Direction.Out: + Out(parameterData); + break; + default: + throw new NotImplementedException($"{parameterData.Parameter.AnyTypeOrVarArgs}: typed record with direction {parameterData.Parameter.Direction} not yet supported"); + } + } + + private static void In(ParameterToManagedData parameterData) + { + var record = (GirModel.Record) parameterData.Parameter.AnyTypeOrVarArgs.AsT0.AsT0; + var variableName = Model.Parameter.GetConvertedName(parameterData.Parameter); + + var signatureName = Model.Parameter.GetName(parameterData.Parameter); + + var ownedHandle = parameterData.Parameter switch + { + { Transfer: GirModel.Transfer.Full } => $"new {Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record)}({signatureName})", + { Transfer: GirModel.Transfer.None } => $"{Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record)}.FromUnowned({signatureName})", + _ => throw new Exception($"Unknown transfer type for typed record parameter {parameterData.Parameter.Name}") + }; + + var nullable = parameterData.Parameter.Nullable + ? $" {signatureName} == IntPtr.Zero ? null :" + : string.Empty; + + parameterData.SetSignatureName(signatureName); + parameterData.SetExpression($"var {variableName} ={nullable} new {Model.TypedRecord.GetFullyQualifiedPublicClassName(record)}({ownedHandle});"); + parameterData.SetCallName(variableName); + } + + private static void Out(ParameterToManagedData parameterData) + { + var managedName = Model.Parameter.GetName(parameterData.Parameter); + var nativeName = Model.Parameter.GetConvertedName(parameterData.Parameter); + + parameterData.SetSignatureName(nativeName); + parameterData.SetCallName($"out var {managedName}"); + + var copy = parameterData.Parameter.Transfer switch + { + Transfer.None => string.Empty, + Transfer.Full => "UnownedCopy().", + _ => throw new NotSupportedException() + }; + + parameterData.SetPostCallExpression(parameterData.Parameter.Nullable + ? $"{nativeName} = {managedName}?.Handle.{copy}DangerousGetHandle() ?? IntPtr.Zero;" + : $"{nativeName} = {managedName}.Handle.{copy}DangerousGetHandle();"); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..1bc32fd95 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/Converter/TypedRecordArray.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Generator.Renderer.Internal.ParameterToManagedExpressions; + +internal class TypedRecordArray : ToManagedParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.IsArray(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToManagedData parameterData, IEnumerable parameters) + { + switch (parameterData.Parameter) + { + case { Direction: GirModel.Direction.In } + when parameterData.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length is not null: + WithLength(parameterData, parameters); + break; + case { Direction: GirModel.Direction.In }: + WithoutLength(parameterData); + break; + default: + throw new Exception($"{parameterData.Parameter}: This kind of typed record array is not yet supported"); + } + } + + private static void WithoutLength(ParameterToManagedData parameter) + { + var parameterName = Model.Parameter.GetName(parameter.Parameter); + parameter.SetSignatureName(parameterName); + parameter.SetCallName($"ref {parameterName}"); + + //TODO + throw new Exception("Test missing for typed record array passed in via a ref to managed"); + } + + private static void WithLength(ParameterToManagedData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + PointerArrayWithLength(parameter, allParameters); + else + StructArrayWithLength(parameter, allParameters); + } + + private static void PointerArrayWithLength(ParameterToManagedData parameter, IEnumerable allParameters) + { + throw new Exception("Pointer array not yet supported for typed record arrays"); + } + + private static void StructArrayWithLength(ParameterToManagedData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.Transfer == GirModel.Transfer.Container || parameter.Parameter.Transfer == GirModel.Transfer.Full) + throw new Exception("Can't transfer ownership to native code for typed record"); + + var record = (GirModel.Record) parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0; + var parameterName = Model.Parameter.GetName(parameter.Parameter); + var nativeVariableName = parameterName + "Native"; + + var lengthIndex = parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length ?? throw new Exception("Length missing"); + var lengthParameter = allParameters.ElementAt(lengthIndex); + + var method = parameter.Parameter.Nullable + ? "ToNullableArray" + : "ToArray"; + + parameter.SetSignatureName(parameterName); + parameter.SetCallName($"{nativeVariableName}.{method}((int){lengthParameter.GetCallName()})"); + + var nullableExpression = parameter.Parameter.Nullable + ? $"{parameterName} == System.IntPtr.Zero ? {Model.TypedRecord.GetFullyQuallifiedArrayNullHandle(record)} : " + : string.Empty; + + parameter.SetExpression($"var {nativeVariableName} = {nullableExpression} new {Model.TypedRecord.GetFullyQuallifiedArrayUnownedHandle(record)}({parameterName}, (int) {lengthParameter.GetCallName()});"); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs index 6831f53a0..f9b175212 100644 --- a/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs +++ b/src/Generation/Generator/Renderer/Internal/ParameterToManagedExpression/ParameterToManagedExpression.cs @@ -25,6 +25,8 @@ internal static class ParameterToManagedExpression new ParameterToManagedExpressions.Record(), new ParameterToManagedExpressions.RecordArray(), new ParameterToManagedExpressions.String(), + new ParameterToManagedExpressions.TypedRecord(), + new ParameterToManagedExpressions.TypedRecordArray(), new ParameterToManagedExpressions.Utf8StringArray(), }; diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecord.cs new file mode 100644 index 000000000..3c92ed109 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecord.cs @@ -0,0 +1,25 @@ +using GirModel; + +namespace Generator.Renderer.Internal.ReturnType; + +internal class TypedRecord : ReturnTypeConverter +{ + public bool Supports(GirModel.ReturnType returnType) + { + return returnType.AnyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableReturnType Convert(GirModel.ReturnType returnType) + { + var type = (GirModel.Record) returnType.AnyType.AsT0; + + var typeName = returnType switch + { + { Transfer: Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedOwnedHandle(type), + _ => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type) + }; + + //Returned SafeHandles are never "null" but "invalid" in case of C NULL. + return new RenderableReturnType(typeName); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..24207c559 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordArray.cs @@ -0,0 +1,17 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal.ReturnType; + +internal class TypedRecordArray : ReturnTypeConverter +{ + public bool Supports(GirModel.ReturnType returnType) + { + return returnType.AnyType.IsArray(out var record) && Model.Record.IsTyped(record); + } + + public RenderableReturnType Convert(GirModel.ReturnType returnType) + { + //Internal arrays of records (SafeHandles) are not supported by the runtime and must be converted via an IntPtr[] + return new RenderableReturnType(Type.PointerArray); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordCallback.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordCallback.cs new file mode 100644 index 000000000..7fa30b97e --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/Converter/TypedRecordCallback.cs @@ -0,0 +1,14 @@ +namespace Generator.Renderer.Internal.ReturnType; + +internal class TypedRecordCallback : ReturnTypeConverter +{ + public bool Supports(GirModel.ReturnType returnType) + { + return returnType.AnyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public RenderableReturnType Convert(GirModel.ReturnType returnType) + { + return new RenderableReturnType(Model.Type.Pointer); + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs index 6e290ee9d..38851dd81 100644 --- a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRenderer.cs @@ -23,6 +23,8 @@ internal static class ReturnTypeRenderer new ReturnType.PrimitiveValueTypeArray(), new ReturnType.Record(), new ReturnType.RecordArray(), + new ReturnType.TypedRecord(), + new ReturnType.TypedRecordArray(), new ReturnType.Union(), new ReturnType.Utf8String(), new ReturnType.Utf8StringArray(), diff --git a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs index 0dd765b1d..403203775 100644 --- a/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs +++ b/src/Generation/Generator/Renderer/Internal/ReturnType/ReturnTypeRendererCallback.cs @@ -22,6 +22,8 @@ internal static class ReturnTypeRendererCallback new ReturnType.PrimitiveValueTypeArray(), new ReturnType.RecordArray(), new ReturnType.RecordInCallback(), + new ReturnType.TypedRecordCallback(), + new ReturnType.TypedRecordArray(), new ReturnType.Union(), new ReturnType.Utf8StringInCallback(), new ReturnType.Utf8StringArrayInCallback(), diff --git a/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..9a9ec1d7a --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/Converter/TypedRecord.cs @@ -0,0 +1,21 @@ +using System; + +namespace Generator.Renderer.Internal.ReturnTypeToNativeExpressions; + +internal class TypedRecord : ReturnTypeConverter +{ + public bool Supports(GirModel.AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public string GetString(GirModel.ReturnType returnType, string fromVariableName) + { + return returnType switch + { + { Transfer: GirModel.Transfer.None, Nullable: true } => $"{fromVariableName}?.Handle.DangerousGetHandle() ?? IntPtr.Zero", + { Transfer: GirModel.Transfer.None, Nullable: false } => $"{fromVariableName}.Handle.DangerousGetHandle()", + { Transfer: GirModel.Transfer.Full, Nullable: true } => $"{fromVariableName}?.Handle.UnownedCopy().DangerousGetHandle() ?? IntPtr.Zero", + { Transfer: GirModel.Transfer.Full, Nullable: false } => $"{fromVariableName}.Handle.UnownedCopy().DangerousGetHandle()", + _ => throw new Exception($"Unknown transfer type for record return type which should be converted to native.") + }; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs index dff6039c5..8b58d46c5 100644 --- a/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs +++ b/src/Generation/Generator/Renderer/Internal/ReturnTypeToNativeExpression/ReturnTypeToNativeExpression.cs @@ -16,6 +16,7 @@ internal static class ReturnTypeToNativeExpression new ReturnTypeToNativeExpressions.PrimitiveValueType(), new ReturnTypeToNativeExpressions.PrimitiveValueTypeAlias(), new ReturnTypeToNativeExpressions.Record(), + new ReturnTypeToNativeExpressions.TypedRecord(), new ReturnTypeToNativeExpressions.Utf8String(), }; diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecord.cs b/src/Generation/Generator/Renderer/Internal/TypedRecord.cs new file mode 100644 index 000000000..d3fa8c473 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecord.cs @@ -0,0 +1,30 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecord +{ + public static string Render(GirModel.Record record) + { + return $@" +using System; +using GObject; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +public partial class {record.Name} +{{ + {Constructors.Render(record.Constructors)} + {Functions.Render(record.TypeFunction)} + {Functions.Render(record.Functions)} + {Methods.Render(record.Methods)} +}}"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecordData.cs b/src/Generation/Generator/Renderer/Internal/TypedRecordData.cs new file mode 100644 index 000000000..573ccd3a6 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecordData.cs @@ -0,0 +1,27 @@ +using Generator.Model; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecordData +{ + public static string Render(GirModel.Record record) + { + return $@" +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +[StructLayout(LayoutKind.Sequential)] +public partial struct {Model.TypedRecord.GetDataName(record)} +{{ + {Fields.Render(record.Fields)} +}}"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecordDelegates.cs b/src/Generation/Generator/Renderer/Internal/TypedRecordDelegates.cs new file mode 100644 index 000000000..af1c04efc --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecordDelegates.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq; +using Generator.Model; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecordDelegates +{ + public static string Render(GirModel.Record record) + { + return $@" +using System; +using System.Runtime.InteropServices; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +public partial struct {Model.TypedRecord.GetDataName(record)} +{{ + {record.Fields + .Select(x => x.AnyTypeOrCallback) + .Where(x => x.IsT1) + .Select(x => x.AsT1) + .Select(Callback.Render) + .Join(Environment.NewLine)} +}}"; + } +} diff --git a/src/Generation/Generator/Renderer/Internal/TypedRecordHandle.cs b/src/Generation/Generator/Renderer/Internal/TypedRecordHandle.cs new file mode 100644 index 000000000..64192cd12 --- /dev/null +++ b/src/Generation/Generator/Renderer/Internal/TypedRecordHandle.cs @@ -0,0 +1,275 @@ +using System; +using System.Linq; +using System.Text; +using Generator.Model; +using Generator.Renderer.Internal.Field; + +namespace Generator.Renderer.Internal; + +internal static class TypedRecordHandle +{ + public static string Render(GirModel.Record record) + { + var typeName = Model.TypedRecord.GetInternalHandle(record); + var dataName = Model.TypedRecord.GetDataName(record); + var unownedHandleTypeName = Model.TypedRecord.GetInternalUnownedHandle(record); + var mangedHandleTypeName = Model.TypedRecord.GetInternalManagedHandle(record); + var ownedHandleTypeName = Model.TypedRecord.GetInternalOwnedHandle(record); + var arrayHandleType = Model.TypedRecord.GetInternalArrayHandle(record); + var arrayUnownedHandleTypeName = Model.TypedRecord.GetInternalArrayUnownedHandle(record); + var arrayOwnedHandleTypeName = Model.TypedRecord.GetInternalArrayOwnedHandle(record); + var fullyQualifiedType = $"{Model.TypedRecord.GetFullyQualifiedPublicClassName(record)}"; + var getGType = $"{Model.TypedRecord.GetFullyQualifiedInternalClassName(record)}.{Function.GetGType}()"; + + return $@"using System; +using GObject; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetInternalName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +public abstract class {typeName} : SafeHandle +{{ + public sealed override bool IsInvalid => handle == IntPtr.Zero; + + protected {typeName}(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) {{ }} + + public {ownedHandleTypeName} OwnedCopy() + {{ + var ptr = GObject.Internal.Functions.BoxedCopy({getGType}, handle); + return new {ownedHandleTypeName}(ptr); + }} + + public {unownedHandleTypeName} UnownedCopy() + {{ + var ptr = GObject.Internal.Functions.BoxedCopy({getGType}, handle); + return new {unownedHandleTypeName}(ptr); + }} + + {record.Fields.Select(x => RenderField(record, x)).Join(Environment.NewLine)} +}} + +public class {unownedHandleTypeName} : {typeName} +{{ + private static {unownedHandleTypeName}? nullHandle; + public static {unownedHandleTypeName} NullHandle => nullHandle ??= new {unownedHandleTypeName}(); + + /// + /// Creates a new instance of {unownedHandleTypeName}. Used automatically by PInvoke. + /// + internal {unownedHandleTypeName}() : base(false) {{ }} + + /// + /// Creates a new instance of {ownedHandleTypeName}. Assumes that the given pointer is unowned by the runtime. + /// + public {unownedHandleTypeName}(IntPtr ptr) : base(false) + {{ + SetHandle(ptr); + }} + + protected override bool ReleaseHandle() + {{ + throw new Exception(""UnownedHandle must not be freed""); + }} +}} + +public class {ownedHandleTypeName} : {typeName} +{{ + /// + /// Creates a new instance of {ownedHandleTypeName}. Used automatically by PInvoke. + /// + internal {ownedHandleTypeName}() : base(true) {{ }} + + /// + /// Creates a new instance of {ownedHandleTypeName}. Assumes that the given pointer is owned by the runtime. + /// + public {ownedHandleTypeName}(IntPtr ptr) : base(true) + {{ + SetHandle(ptr); + }} + + /// + /// Create a {ownedHandleTypeName} from a pointer that is assumed unowned. To do so a + /// boxed copy is created of the given pointer to be used as the handle. + /// + /// A pointer to a {record.Name} which is not owned by the runtime. + /// A {ownedHandleTypeName} + public static {ownedHandleTypeName} FromUnowned(IntPtr ptr) + {{ + var ownedPtr = GObject.Internal.Functions.BoxedCopy({getGType}, ptr); + return new {ownedHandleTypeName}(ownedPtr); + }} + + protected override bool ReleaseHandle() + {{ + GObject.Internal.Functions.BoxedFree({getGType}, handle); + return true; + }} +}} + +public class {mangedHandleTypeName} : {ownedHandleTypeName} +{{ + private {mangedHandleTypeName}(IntPtr handle) : base(handle) + {{ + }} + + public static {mangedHandleTypeName} Create() + {{ + var size = Marshal.SizeOf<{dataName}>(); + IntPtr ptr = Marshal.AllocHGlobal(size); + + var str = new {dataName}(); + Marshal.StructureToPtr(str, ptr, false); + + return new {mangedHandleTypeName}(ptr); + }} + + protected override bool ReleaseHandle() + {{ + GObject.Internal.Functions.BoxedFree({getGType}, handle); + return true; + }} +}} + +public abstract class {arrayHandleType} : SafeHandle +{{ + public sealed override bool IsInvalid => handle == IntPtr.Zero; + + protected {arrayHandleType}(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) {{ }} +}} + +public class {arrayUnownedHandleTypeName} : {arrayHandleType} +{{ + private static {arrayUnownedHandleTypeName}? nullHandle; + public static {arrayUnownedHandleTypeName} NullHandle => nullHandle ??= new {arrayUnownedHandleTypeName}(); + + private int length; + + /// + /// Creates a new instance of {arrayUnownedHandleTypeName}. Used automatically by PInvoke. + /// + internal {arrayUnownedHandleTypeName}() : base(false) {{ }} + + public {arrayUnownedHandleTypeName}(IntPtr ptr, int length) : base(false) + {{ + this.length = length; + SetHandle(ptr); + }} + + public {fullyQualifiedType}[] ToArray(int length) + {{ + return ToNullableArray(length) ?? throw new InvalidOperationException(""Handle is invalid""); + }} + + public {fullyQualifiedType}[]? ToNullableArray(int length) + {{ + if (IsInvalid) + return null; + + var data = new {fullyQualifiedType}[length]; + var currentHandle = handle; + for(int i = 0; i < length; i++) + {{ + var ownedHandle = new {Model.TypedRecord.GetFullyQuallifiedUnownedHandle(record)}(currentHandle).OwnedCopy(); + data[i] = new {fullyQualifiedType}(ownedHandle); + currentHandle += Marshal.SizeOf<{Model.TypedRecord.GetFullyQuallifiedDataName(record)}>(); + }} + + return data; + }} + + protected override bool ReleaseHandle() + {{ + throw new Exception(""UnownedHandle must not be freed""); + }} +}} + +public class {arrayOwnedHandleTypeName} : {arrayHandleType} +{{ + + //This has no constructor without parameters as we can't supply a length to an array via pinvoke. + //The length would need to be set manually and the instance be freed via glib. + + private {arrayOwnedHandleTypeName}(IntPtr ptr) : base(true) + {{ + SetHandle(ptr); + }} + + public static {arrayOwnedHandleTypeName} Create({Model.TypedRecord.GetFullyQualifiedPublicClassName(record)}[] data) + {{ + var size = Marshal.SizeOf<{Model.TypedRecord.GetFullyQuallifiedDataName(record)}>(); + var ptr = Marshal.AllocHGlobal(size * data.Length); + var current = ptr; + for (int i = 0; i < data.Length; i++) + {{ + Marshal.StructureToPtr(data[i], current, false); + current += size; + }} + + return new {arrayOwnedHandleTypeName}(ptr); + }} + + protected override bool ReleaseHandle() + {{ + Marshal.FreeHGlobal(handle); + return true; + }} +}}"; + } + + private static string RenderField(GirModel.Record record, GirModel.Field field) + { + var renderableField = Fields.GetRenderableField(field); + + if (field is { IsReadable: false, IsWritable: false } || field.IsPrivate) + return string.Empty; + + var result = new StringBuilder(); + + if (field.IsReadable) + result.AppendLine(RenderFieldGetter(record, field, renderableField)); + + if (field.IsWritable) + result.AppendLine(RenderFieldSetter(record, field, renderableField)); + + return result.ToString(); + + } + + private static string RenderFieldGetter(GirModel.Record record, GirModel.Field field, RenderableField renderableField) + { + var typePrefix = field.AnyTypeOrCallback.IsT1 ? $"{Model.TypedRecord.GetDataName(record)}." : string.Empty; + var dataName = Model.TypedRecord.GetDataName(record); + + return @$"public unsafe {typePrefix}{renderableField.NullableTypeName} Get{renderableField.Name}() +{{ + if (IsClosed || IsInvalid) + throw new InvalidOperationException(""Handle is closed or invalid""); + + var data = Unsafe.AsRef<{dataName}>((void*)handle); + return data.{renderableField.Name}; +}}"; + } + + private static string RenderFieldSetter(GirModel.Record record, GirModel.Field field, RenderableField renderableField) + { + var dataName = Model.TypedRecord.GetDataName(record); + + return @$"public unsafe void Set{renderableField.Name}({renderableField.NullableTypeName} value) +{{ + if (IsClosed || IsInvalid) + throw new InvalidOperationException(""Handle is closed or invalid""); + + var data = Unsafe.AsRef<{dataName}>((void*)handle); + data.{renderableField.Name} = value; +}}"; + } + + +} diff --git a/src/Generation/Generator/Renderer/Public/Callback/CallbackDelegate.cs b/src/Generation/Generator/Renderer/Public/Callback/CallbackDelegate.cs index 3f991366b..47691ca8c 100644 --- a/src/Generation/Generator/Renderer/Public/Callback/CallbackDelegate.cs +++ b/src/Generation/Generator/Renderer/Public/Callback/CallbackDelegate.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Generator.Model; namespace Generator.Renderer.Public; diff --git a/src/Generation/Generator/Renderer/Public/Class/Signals/ClassSignal.Parameter.cs b/src/Generation/Generator/Renderer/Public/Class/Signals/ClassSignal.Parameter.cs index ba8b24baa..5c3861a8a 100644 --- a/src/Generation/Generator/Renderer/Public/Class/Signals/ClassSignal.Parameter.cs +++ b/src/Generation/Generator/Renderer/Public/Class/Signals/ClassSignal.Parameter.cs @@ -23,6 +23,6 @@ private static string RenderAsSignalParammeter(GirModel.Parameter parameter, int { var p = ParameterRenderer.Render(parameter); - return $@"public {p.NullableTypeName} {parameter.Name.ToPascalCase()} => Args[{index}].Extract<{p.NullableTypeName}>();"; + return $@"public {p.NullableTypeName} {parameter.Name.ToPascalCase()} => Extract<{p.NullableTypeName}>(Args[{index}]);"; } } diff --git a/src/Generation/Generator/Renderer/Public/Constructor/ConstructorRenderer.cs b/src/Generation/Generator/Renderer/Public/Constructor/ConstructorRenderer.cs index 4404f3552..a7a70c5a9 100644 --- a/src/Generation/Generator/Renderer/Public/Constructor/ConstructorRenderer.cs +++ b/src/Generation/Generator/Renderer/Public/Constructor/ConstructorRenderer.cs @@ -12,6 +12,7 @@ internal static class ConstructorRenderer new Constructor.Class(), new Constructor.OpaqueTypedRecord(), new Constructor.OpaqueUntypedRecord(), + new Constructor.TypedRecord(), }; public static string Render(GirModel.Constructor constructor) @@ -100,7 +101,13 @@ private static string RenderCallStatement(GirModel.Constructor constructor, IEnu call.Append(");" + Environment.NewLine); call.Append(Error.RenderThrowOnError(constructor)); - call.Append($"return {data.GetCreateExpression(constructor, variableName)};"); + + var postCallExpressions = parameters.Select(x => x.GetPostCallExpression()) + .Where(x => !string.IsNullOrEmpty(x)) + .Cast(); + + call.AppendJoin(Environment.NewLine, postCallExpressions); + call.AppendLine($"return {data.GetCreateExpression(constructor, variableName)};"); return call.ToString(); } diff --git a/src/Generation/Generator/Renderer/Public/Constructor/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/Constructor/Converter/TypedRecord.cs new file mode 100644 index 000000000..346be76b2 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/Constructor/Converter/TypedRecord.cs @@ -0,0 +1,32 @@ +namespace Generator.Renderer.Public.Constructor; + +public class TypedRecord : ConstructorConverter +{ + public bool Supports(GirModel.Constructor constructor) + { + return constructor.Parent is GirModel.Record record && Model.Record.IsTyped(record); + } + + public ConstructorData GetData(GirModel.Constructor constructor) + { + return new( + RequiresNewModifier: false, + GetCreateExpression: CreateExpression, + + //Constructors which do not transfer ownership likely create floating references. + //as there is no way to know how to sink those references those constructors are not rendered + //automatically as part of the public api and must be implemented manually. + AllowRendering: constructor.ReturnType.Transfer == GirModel.Transfer.Full + ); + } + + private static string CreateExpression(GirModel.Constructor constructor, string fromVariableName) + { + var record = (GirModel.Record) constructor.Parent; + var createInstance = $"new {record.Name}({fromVariableName})"; + + return constructor.ReturnType.Nullable + ? $"{fromVariableName}.IsInvalid ? null : {createInstance}" + : createInstance; + } +} diff --git a/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..1cbe05c7b --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/Converter/TypedRecord.cs @@ -0,0 +1,21 @@ +using System; + +namespace Generator.Renderer.Public.InstanceParameterToNativeExpressions; + +public class TypedRecord : InstanceParameterConverter +{ + public bool Supports(GirModel.Type type) + { + return type is GirModel.Record record && Model.Record.IsTyped(record); + } + + public string GetExpression(GirModel.InstanceParameter instanceParameter) + { + return instanceParameter switch + { + { Transfer: GirModel.Transfer.None } => "this.Handle", + { Transfer: GirModel.Transfer.Full } => "this.Handle.UnownedCopy()", + _ => throw new Exception("Unknown transfer type for opaque untyped instance parameter") + }; + } +} diff --git a/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs index 92bf98a63..e40c98d7b 100644 --- a/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs +++ b/src/Generation/Generator/Renderer/Public/InstanceParameterToNativeExpression/InstanceParameterToNativeExpression.cs @@ -11,6 +11,7 @@ internal static class InstanceParameterToNativeExpression new InstanceParameterToNativeExpressions.OpaqueTypedRecord(), new InstanceParameterToNativeExpressions.OpaqueUntypedRecord(), new InstanceParameterToNativeExpressions.Pointer(), + new InstanceParameterToNativeExpressions.TypedRecord() }; public static string Render(GirModel.InstanceParameter instanceParameter) diff --git a/src/Generation/Generator/Renderer/Public/Interface/InterfaceMethods.cs b/src/Generation/Generator/Renderer/Public/Interface/InterfaceMethods.cs index 581803fb6..c7e293d0a 100644 --- a/src/Generation/Generator/Renderer/Public/Interface/InterfaceMethods.cs +++ b/src/Generation/Generator/Renderer/Public/Interface/InterfaceMethods.cs @@ -57,6 +57,12 @@ private static string RenderParameters(IEnumerable parame if (parameter.IsDestroyNotify) continue; + if (parameter.IsArrayLengthParameter) + continue; + + if (parameter.IsGLibErrorParameter) + continue; + var typeData = ParameterRenderer.Render(parameter.Parameter); result.Add($"{typeData.Direction}{typeData.NullableTypeName} {parameter.GetSignatureName()}"); } diff --git a/src/Generation/Generator/Renderer/Public/Parameter/Converter/Pointer.cs b/src/Generation/Generator/Renderer/Public/Parameter/Converter/Pointer.cs index e1d4b590c..1a2b84846 100644 --- a/src/Generation/Generator/Renderer/Public/Parameter/Converter/Pointer.cs +++ b/src/Generation/Generator/Renderer/Public/Parameter/Converter/Pointer.cs @@ -19,7 +19,6 @@ public ParameterTypeData Create(GirModel.Parameter parameter) private static string GetDirection(GirModel.Parameter parameter) => parameter switch { { Direction: GirModel.Direction.InOut } => ParameterDirection.Ref(), - { Direction: GirModel.Direction.Out, CallerAllocates: true } => ParameterDirection.Ref(), { Direction: GirModel.Direction.Out } => ParameterDirection.Out(), _ => ParameterDirection.In() }; diff --git a/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecord.cs new file mode 100644 index 000000000..9f0432409 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecord.cs @@ -0,0 +1,32 @@ +using System; + +namespace Generator.Renderer.Public.Parameter; + +internal class TypedRecord : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.Is(out var record) && Model.Record.IsTyped(record); + } + + public ParameterTypeData Create(GirModel.Parameter parameter) + { + return new ParameterTypeData( + Direction: GetDirection(parameter), + NullableTypeName: GetNullableTypeName(parameter) + ); + } + + private static string GetNullableTypeName(GirModel.Parameter parameter) + { + var type = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT0; + return Model.TypedRecord.GetFullyQualifiedPublicClassName(type) + Nullable.Render(parameter); + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + { Direction: GirModel.Direction.Out } => ParameterDirection.Out(), + _ => throw new Exception($"records with direction '{parameter.Direction}' not yet supported") + }; +} diff --git a/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..fe5eb1130 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/Parameter/Converter/TypedRecordArray.cs @@ -0,0 +1,31 @@ +using System; + +namespace Generator.Renderer.Public.Parameter; + +internal class TypedRecordArray : ParameterConverter +{ + public bool Supports(GirModel.AnyType anyType) + { + return anyType.IsArray(out var record) && Model.Record.IsTyped(record); + } + + public ParameterTypeData Create(GirModel.Parameter parameter) + { + return new ParameterTypeData( + Direction: GetDirection(parameter), + NullableTypeName: GetNullableTypeName(parameter) + ); + } + + private static string GetNullableTypeName(GirModel.Parameter parameter) + { + var arrayType = parameter.AnyTypeOrVarArgs.AsT0.AsT1; + return $"{Model.TypedRecord.GetFullyQualifiedPublicClassName((GirModel.Record) arrayType.AnyType.AsT0)}[]{Nullable.Render(parameter)}"; + } + + private static string GetDirection(GirModel.Parameter parameter) => parameter switch + { + { Direction: GirModel.Direction.In } => ParameterDirection.In(), + _ => throw new Exception($"Unknown direction for typed record in parameter {parameter.Name}.") + }; +} diff --git a/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs b/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs index e1f863ff6..742e47424 100644 --- a/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs +++ b/src/Generation/Generator/Renderer/Public/Parameter/ParameterRenderer.cs @@ -28,6 +28,8 @@ internal static class ParameterRenderer new Parameter.RecordArray(), new Parameter.String(), new Parameter.StringArray(), + new Parameter.TypedRecord(), + new Parameter.TypedRecordArray(), new Parameter.Union(), new Parameter.Void(), }; diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Pointer.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Pointer.cs index 35ec3e60e..a9a64664e 100644 --- a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Pointer.cs +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Pointer.cs @@ -38,14 +38,13 @@ public void Initialize(ParameterToNativeData parameterData, IEnumerable parameter switch { { Direction: GirModel.Direction.InOut } => ParameterDirection.Ref(), - { Direction: GirModel.Direction.Out, CallerAllocates: true } => ParameterDirection.Ref(), { Direction: GirModel.Direction.Out } => ParameterDirection.Out(), _ => ParameterDirection.In() }; diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..f1bfc8e7f --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecord.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; + +namespace Generator.Renderer.Public.ParameterToNativeExpressions; + +internal class TypedRecord : ToNativeParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToNativeData parameter, IEnumerable _) + { + if (Model.Parameter.IsGLibError(parameter.Parameter)) + ErrorRecord(parameter); + else + RegularRecord(parameter); + } + + private static void ErrorRecord(ParameterToNativeData parameterData) + { + parameterData.IsGLibErrorParameter = true; + + var name = Model.Parameter.GetName(parameterData.Parameter); + parameterData.SetSignatureName(name); + parameterData.SetCallName($"out var {name}"); + } + + private static void RegularRecord(ParameterToNativeData parameter) + { + if (parameter.Parameter.Direction != GirModel.Direction.In) + throw new NotImplementedException($"{parameter.Parameter.AnyTypeOrVarArgs}: record parameter '{parameter.Parameter.Name}' with direction != in not yet supported"); + + var record = (GirModel.Record) parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT0; + var typeHandle = Model.TypedRecord.GetFullyQuallifiedHandle(record); + var nullHandle = Model.TypedRecord.GetFullyQuallifiedNullHandle(record); + var signatureName = Model.Parameter.GetName(parameter.Parameter); + + var callName = parameter.Parameter switch + { + { Nullable: true, Transfer: GirModel.Transfer.None } => $"({typeHandle}?) {signatureName}?.Handle ?? {nullHandle}", + { Nullable: false, Transfer: GirModel.Transfer.None } => $"{signatureName}.Handle", + { Nullable: true, Transfer: GirModel.Transfer.Full } => $"{signatureName}?.Handle.UnownedCopy() ?? {nullHandle}", + { Nullable: false, Transfer: GirModel.Transfer.Full } => $"{signatureName}.Handle.UnownedCopy()", + _ => throw new Exception($"Can't detect call name for parameter record parameter {parameter.Parameter.Name}") + }; + + parameter.SetSignatureName(signatureName); + parameter.SetCallName(callName); + } +} diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..e7beeaf7e --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/TypedRecordArray.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Generator.Renderer.Public.ParameterToNativeExpressions; + +internal class TypedRecordArray : ToNativeParameterConverter +{ + public bool Supports(GirModel.AnyType type) + => type.IsArray(out var record) && Model.Record.IsTyped(record); + + public void Initialize(ParameterToNativeData parameterData, IEnumerable parameters) + { + switch (parameterData.Parameter) + { + case { Direction: GirModel.Direction.In } + when parameterData.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length is not null: + Span(parameterData, parameters); + break; + case { Direction: GirModel.Direction.In }: + Ref(parameterData); + break; + default: + throw new Exception($"{parameterData.Parameter}: This kind of typed record array is not yet supported"); + } + } + + private static void Ref(ParameterToNativeData parameter) + { + var parameterName = Model.Parameter.GetName(parameter.Parameter); + parameter.SetSignatureName(parameterName); + parameter.SetCallName($"ref {parameterName}"); + + //TODO + throw new Exception("Test missing for typed record array passed in via a ref"); + } + + private static void Span(ParameterToNativeData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer) + PointerArray(parameter, allParameters); + else + StructArray(parameter, allParameters); + } + + private static void PointerArray(ParameterToNativeData parameter, IEnumerable allParameters) + { + var parameterName = Model.Parameter.GetName(parameter.Parameter); + var nativeVariableName = parameterName + "Native"; + + parameter.SetSignatureName(parameterName); + parameter.SetCallName($"ref MemoryMarshal.GetReference({nativeVariableName})"); + + var nullable = parameter.Parameter.Nullable + ? $"{parameterName} is null ? null : " + : string.Empty; + + parameter.SetExpression($"var {nativeVariableName} = new Span({nullable}{parameterName}" + + $".Select(record => record.Handle.DangerousGetHandle()).ToArray());"); + + var lengthIndex = parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length ?? throw new Exception("Length missing"); + var lengthParameter = allParameters.ElementAt(lengthIndex); + var lengthParameterType = Model.Type.GetName(lengthParameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT0); + + switch (lengthParameter.Parameter.Direction) + { + case GirModel.Direction.In: + lengthParameter.IsArrayLengthParameter = true; + lengthParameter.SetCallName(parameter.Parameter.Nullable + ? $"({lengthParameterType}) ({parameterName}?.Length ?? 0)" + : $"({lengthParameterType}) {parameterName}.Length" + ); + break; + default: + throw new Exception("Unknown direction for length parameter in typed record array"); + } + } + + private static void StructArray(ParameterToNativeData parameter, IEnumerable allParameters) + { + if (parameter.Parameter.Transfer == GirModel.Transfer.Container || parameter.Parameter.Transfer == GirModel.Transfer.Full) + throw new Exception("Can't transfer ownership to native code for typed record"); + + var record = (GirModel.Record) parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0; + var parameterName = Model.Parameter.GetName(parameter.Parameter); + var nativeVariableName = parameterName + "Native"; + + parameter.SetSignatureName(parameterName); + parameter.SetCallName(nativeVariableName); + + var nullable = parameter.Parameter.Nullable + ? $"{parameterName} is null ? ({Model.TypedRecord.GetFullyQuallifiedArrayHandle(record)}){Model.TypedRecord.GetFullyQuallifiedArrayNullHandle(record)} : " + : string.Empty; + + parameter.SetExpression($"var {nativeVariableName} = {nullable} {Model.TypedRecord.GetFullyQuallifiedArrayOwnedHandle(record)}.Create({parameterName});"); + + var lengthIndex = parameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT1.Length ?? throw new Exception("Length missing"); + var lengthParameter = allParameters.ElementAt(lengthIndex); + var lengthParameterType = Model.Type.GetName(lengthParameter.Parameter.AnyTypeOrVarArgs.AsT0.AsT0); + + switch (lengthParameter.Parameter.Direction) + { + case GirModel.Direction.In: + lengthParameter.IsArrayLengthParameter = true; + lengthParameter.SetCallName(parameter.Parameter.Nullable + ? $"({lengthParameterType}) ({parameterName}?.Length ?? 0)" + : $"({lengthParameterType}) {parameterName}.Length" + ); + break; + default: + throw new Exception("Unknown direction for length parameter in typed record array"); + } + } +} diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Utf8String.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Utf8String.cs index 67b43819c..2e630d498 100644 --- a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Utf8String.cs +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/Converter/Utf8String.cs @@ -16,40 +16,54 @@ public void Initialize(ParameterToNativeData parameter, IEnumerable Model.Utf8String.GetInternalNullableUnownedHandleName(), - { Nullable: true, Transfer: GirModel.Transfer.None } => Model.Utf8String.GetInternalNullableOwnedHandleName(), - { Nullable: false, Transfer: GirModel.Transfer.Full } => Model.Utf8String.GetInternalNonNullableUnownedHandleName(), - { Nullable: false, Transfer: GirModel.Transfer.None } => Model.Utf8String.GetInternalNonNullableOwnedHandleName(), - _ => throw new Exception($"Parameter {parameter.Parameter.Name} of type {parameter.Parameter.AnyTypeOrVarArgs} not supported") - }; - - parameter.SetExpression($"using var {nativeVariableName} = {ownedHandleTypeName}.Create({parameterName});"); - parameter.SetCallName(nativeVariableName); - } + { Nullable: true, Transfer: GirModel.Transfer.Full } => Model.Utf8String.GetInternalNullableUnownedHandleName(), + { Nullable: true, Transfer: GirModel.Transfer.None } => Model.Utf8String.GetInternalNullableOwnedHandleName(), + { Nullable: false, Transfer: GirModel.Transfer.Full } => Model.Utf8String.GetInternalNonNullableUnownedHandleName(), + { Nullable: false, Transfer: GirModel.Transfer.None } => Model.Utf8String.GetInternalNonNullableOwnedHandleName(), + _ => throw new Exception($"Parameter {parameter.Parameter.Name} of type {parameter.Parameter.AnyTypeOrVarArgs} not supported") + }; + + parameter.SetExpression($"using var {nativeVariableName} = {ownedHandleTypeName}.Create({parameterName});"); + parameter.SetCallName(nativeVariableName); + } + + private static void Out(ParameterToNativeData parameter) + { + var nativeVariableName = Model.Parameter.GetConvertedName(parameter.Parameter); + var parameterName = Model.Parameter.GetName(parameter.Parameter); + parameter.SetSignatureName(parameterName); + // Note: optional parameters are generated as regular out parameters, which the caller can ignore with 'out var _' if desired. + parameter.SetCallName($"out var {nativeVariableName}"); + + // After the call, convert the resulting handle to a managed string and free the native memory right away. + var expression = new StringBuilder(); + expression.AppendLine($"{parameterName} = {nativeVariableName}.ConvertToString();"); + expression.Append($"{nativeVariableName}.Dispose();"); + parameter.SetPostCallExpression(expression.ToString()); } } diff --git a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs index 1c2da09e0..19143d1d6 100644 --- a/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs +++ b/src/Generation/Generator/Renderer/Public/ParameterToNativeExpression/ParameterToNativeExpression.cs @@ -28,6 +28,8 @@ internal static class ParameterToNativeExpression new ParameterToNativeExpressions.PrimitiveValueTypeArray(), new ParameterToNativeExpressions.Record(), new ParameterToNativeExpressions.RecordArray(), + new ParameterToNativeExpressions.TypedRecord(), + new ParameterToNativeExpressions.TypedRecordArray(), new ParameterToNativeExpressions.Utf8String(), new ParameterToNativeExpressions.Utf8StringArray(), }; diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecord.cs new file mode 100644 index 000000000..be399d460 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecord.cs @@ -0,0 +1,16 @@ +using Generator.Model; + +namespace Generator.Renderer.Public.ReturnType; + +internal class TypedRecord : ReturnTypeConverter +{ + public RenderableReturnType Create(GirModel.ReturnType returnType) + { + var typeName = ComplexType.GetFullyQualified((GirModel.Record) returnType.AnyType.AsT0); + + return new RenderableReturnType(typeName + Nullable.Render(returnType)); + } + + public bool Supports(GirModel.ReturnType returnType) + => returnType.AnyType.Is(out var record) && Model.Record.IsTyped(record); +} diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecordArray.cs b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecordArray.cs new file mode 100644 index 000000000..f0a464804 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ReturnType/Converter/TypedRecordArray.cs @@ -0,0 +1,14 @@ +using Generator.Model; + +namespace Generator.Renderer.Public.ReturnType; + +internal class TypedRecordArray : ReturnTypeConverter +{ + public RenderableReturnType Create(GirModel.ReturnType returnType) + { + return new RenderableReturnType(ArrayType.GetName(returnType.AnyType.AsT1)); + } + + public bool Supports(GirModel.ReturnType returnType) + => returnType.AnyType.IsArray(out var record) && Model.Record.IsTyped(record); +} diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs index 7ddfa13da..9cf0d1411 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRenderer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Reflection.Metadata.Ecma335; namespace Generator.Renderer.Public; @@ -20,6 +21,8 @@ internal static class ReturnTypeRenderer new ReturnType.RecordArray(), new ReturnType.String(), new ReturnType.StringArray(), + new ReturnType.TypedRecord(), + new ReturnType.TypedRecordArray(), new ReturnType.Void(), }; diff --git a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs index 5742eba14..9e006abfd 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnType/ReturnTypeRendererCallback.cs @@ -18,8 +18,10 @@ internal static class ReturnTypeRendererCallback new ReturnType.PrimitiveValueTypeAlias(), new ReturnType.Record(), new ReturnType.RecordArray(), - new ReturnType.StringInCallback(), new ReturnType.StringArray(), + new ReturnType.StringInCallback(), + new ReturnType.TypedRecord(), + new ReturnType.TypedRecordArray(), new ReturnType.Void(), }; diff --git a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/TypedRecord.cs new file mode 100644 index 000000000..7b6c7f9a9 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/Converter/TypedRecord.cs @@ -0,0 +1,28 @@ +using System; +using GirModel; + +namespace Generator.Renderer.Public.ReturnTypeToManagedExpressions; + +internal class TypedRecord : ReturnTypeConverter +{ + public bool Supports(AnyType type) + => type.Is(out var record) && Model.Record.IsTyped(record); + + public string GetString(GirModel.ReturnType returnType, string fromVariableName) + { + var record = (GirModel.Record) returnType.AnyType.AsT0; + + var handleExpression = returnType switch + { + { Transfer: Transfer.Full } => fromVariableName, + { Transfer: Transfer.None } => $"{fromVariableName}.OwnedCopy()", + _ => throw new NotImplementedException("Unknown transfer type") + }; + + var createNewInstance = $"new {Model.ComplexType.GetFullyQualified(record)}({handleExpression})"; + + return returnType.Nullable + ? $"{fromVariableName}.IsInvalid ? null : {createNewInstance};" + : createNewInstance; + } +} diff --git a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs index 0546d7f24..a147ffbc4 100644 --- a/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs +++ b/src/Generation/Generator/Renderer/Public/ReturnTypeToManagedExpression/ReturnTypeToManagedExpression.cs @@ -20,6 +20,7 @@ internal static class ReturnTypeToManagedExpression new ReturnTypeToManagedExpressions.PrimitiveValueTypeAlias(), new ReturnTypeToManagedExpressions.PrimitiveValueTypeArray(), new ReturnTypeToManagedExpressions.Record(), + new ReturnTypeToManagedExpressions.TypedRecord(), new ReturnTypeToManagedExpressions.Utf8String(), new ReturnTypeToManagedExpressions.Utf8StringArray(), }; diff --git a/src/Generation/Generator/Renderer/Public/TypedRecord.cs b/src/Generation/Generator/Renderer/Public/TypedRecord.cs new file mode 100644 index 000000000..f8c3b81e2 --- /dev/null +++ b/src/Generation/Generator/Renderer/Public/TypedRecord.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using Generator.Model; + +namespace Generator.Renderer.Public; + +internal static class TypedRecord +{ + public static string Render(GirModel.Record record) + { + var name = Model.TypedRecord.GetPublicClassName(record); + var internalHandleName = Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record); + + return $@" +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +#nullable enable + +namespace {Namespace.GetPublicName(record.Namespace)}; + +// AUTOGENERATED FILE - DO NOT MODIFY + +{PlatformSupportAttribute.Render(record as GirModel.PlatformDependent)} +public partial class {name} +{{ + public {internalHandleName} Handle {{ get; }} + + public {name}({internalHandleName} handle) + {{ + Handle = handle; + Initialize(); + }} + + //TODO: This is a workaround constructor as long as we are + //not having https://github.com/gircore/gir.core/issues/397 + private {name}(IntPtr ptr, bool ownsHandle) : this(ownsHandle + ? new {Model.OpaqueTypedRecord.GetFullyQuallifiedOwnedHandle(record)}(ptr) + : new {Model.OpaqueTypedRecord.GetFullyQuallifiedUnownedHandle(record)}(ptr).OwnedCopy()){{ }} + + // Implement this to perform additional steps in the constructor + partial void Initialize(); + + {record.Constructors + .Select(ConstructorRenderer.Render) + .Join(Environment.NewLine)} + + {FunctionRenderer.Render(record.TypeFunction)} + + {record.Functions + .Select(FunctionRenderer.Render) + .Join(Environment.NewLine)} + + {record.Methods + .Where(Method.IsEnabled) + .Select(MethodRenderer.Render) + .Join(Environment.NewLine)} +}}"; + } +} diff --git a/src/Generation/GirLoader/Output/Class.cs b/src/Generation/GirLoader/Output/Class.cs index ab2925885..28079da3a 100644 --- a/src/Generation/GirLoader/Output/Class.cs +++ b/src/Generation/GirLoader/Output/Class.cs @@ -67,5 +67,5 @@ internal override bool Matches(TypeReference typeReference) } public override string ToString() - => Name; + => $"{Repository.Namespace.Name}.{Name}"; } diff --git a/src/Generation/GirLoader/Output/Interface.cs b/src/Generation/GirLoader/Output/Interface.cs index 039e50fe8..c1401a97f 100644 --- a/src/Generation/GirLoader/Output/Interface.cs +++ b/src/Generation/GirLoader/Output/Interface.cs @@ -44,4 +44,7 @@ internal override bool Matches(TypeReference typeReference) return false; } + + public override string ToString() + => $"{Repository.Namespace.Name}.{Name}"; } diff --git a/src/Generation/GirLoader/Output/Record.cs b/src/Generation/GirLoader/Output/Record.cs index 15de9c692..59c998449 100644 --- a/src/Generation/GirLoader/Output/Record.cs +++ b/src/Generation/GirLoader/Output/Record.cs @@ -49,4 +49,7 @@ internal override bool Matches(TypeReference typeReference) return ctypeMatches || (symbolNameMatches && (namespaceMatches || namespaceMissing)); } + + public override string ToString() + => $"{Repository.Namespace.Name}.{Name}"; } diff --git a/src/GirCore.Libraries.props b/src/GirCore.Libraries.props index ae8e1fbed..15cc5ae84 100644 --- a/src/GirCore.Libraries.props +++ b/src/GirCore.Libraries.props @@ -4,5 +4,6 @@ net7.0;net6.0 enable true + true \ No newline at end of file diff --git a/src/Libs/GLib-2.0/Public/Source.cs b/src/Libs/GLib-2.0/Public/Source.cs deleted file mode 100644 index d401bc8d6..000000000 --- a/src/Libs/GLib-2.0/Public/Source.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace GLib; - -public partial class Source -{ - public void SetCallback(SourceFunc sourceFunc) - { - var handler = new Internal.SourceFuncNotifiedHandler(sourceFunc); - Internal.Source.SetCallback(Handle, handler.NativeCallback, IntPtr.Zero, handler.DestroyNotify); - } - - public void Attach(MainContext mainContext) - { - Internal.Source.Attach(Handle, mainContext.Handle); - } - - public static void Remove(uint tag) => Internal.Functions.SourceRemove(tag); -} diff --git a/src/Libs/GObject-2.0/GObject-2.0.csproj b/src/Libs/GObject-2.0/GObject-2.0.csproj index 7c2c3f80d..b43321952 100644 --- a/src/Libs/GObject-2.0/GObject-2.0.csproj +++ b/src/Libs/GObject-2.0/GObject-2.0.csproj @@ -10,8 +10,7 @@ - - <_Parameter1>GObject-2.0.Tests - + + diff --git a/src/Libs/GObject-2.0/Public/Closure.cs b/src/Libs/GObject-2.0/Public/Closure.cs index afef79ece..06744b02e 100644 --- a/src/Libs/GObject-2.0/Public/Closure.cs +++ b/src/Libs/GObject-2.0/Public/Closure.cs @@ -15,36 +15,33 @@ public partial class Closure : IDisposable internal Closure(ClosureCallback callback) { _callback = callback; - _handle = Internal.Closure.NewSimple((uint) Marshal.SizeOf(), IntPtr.Zero); + Handle = Internal.Closure.NewSimple((uint) Marshal.SizeOf(), IntPtr.Zero); - Debug.WriteLine($"Instantiating Closure: Address {_handle.DangerousGetHandle()}."); + Debug.WriteLine($"Instantiating Closure: Address {Handle.DangerousGetHandle()}."); _closureMarshal = InternalCallback; //Save delegate to keep instance alive - Internal.Closure.Ref(_handle); - Internal.Closure.Sink(_handle); - Internal.Closure.SetMarshal(_handle, _closureMarshal); + Internal.Closure.Ref(Handle); + Internal.Closure.Sink(Handle); + Internal.Closure.SetMarshal(Handle, _closureMarshal); } - private void InternalCallback(IntPtr closure, IntPtr returnValuePtr, uint nParamValues, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] Internal.ValueData[] paramValuesData, IntPtr invocationHint, IntPtr userData) + private void InternalCallback(IntPtr closure, IntPtr returnValuePtr, uint nParamValues, IntPtr paramValuesData, IntPtr invocationHint, IntPtr userData) { - Debug.Assert( - condition: paramValuesData.Length == nParamValues, - message: "Values were not marshalled correctly. Breakage may occur" - ); - + var returnUnownedHandle = new Internal.ValueUnownedHandle(returnValuePtr); var returnValue = returnValuePtr != IntPtr.Zero - ? new Value(new Internal.ValueUnownedHandle(returnValuePtr)) + ? new Value(returnUnownedHandle.OwnedCopy()) : null; - var paramValues = paramValuesData - .Select(valueData => Internal.ValueManagedHandle.Create(valueData)) - .Select(valueHandle => new Value(valueHandle)) - .ToArray(); + var paramValues = new Internal.ValueArray2UnownedHandle(paramValuesData, (int) nParamValues).ToArray((int) nParamValues); try { _callback(returnValue, paramValues); + + //The result must be copied back into the original value as the callback operates on a copy + if (returnValue is not null && !returnUnownedHandle.IsInvalid) + Internal.Value.Copy(returnValue.Handle, returnUnownedHandle); } catch (Exception e) { @@ -54,7 +51,7 @@ private void InternalCallback(IntPtr closure, IntPtr returnValuePtr, uint nParam public void Dispose() { - Debug.WriteLine($"Disposing Closure: Address {_handle.DangerousGetHandle()}."); - _handle.Dispose(); + Debug.WriteLine($"Disposing Closure: Address {Handle.DangerousGetHandle()}."); + Handle.Dispose(); } } diff --git a/src/Libs/GObject-2.0/Public/ConstructArgument.cs b/src/Libs/GObject-2.0/Public/ConstructArgument.cs index df74eb9d7..416c2ace6 100644 --- a/src/Libs/GObject-2.0/Public/ConstructArgument.cs +++ b/src/Libs/GObject-2.0/Public/ConstructArgument.cs @@ -17,32 +17,10 @@ public sealed class ConstructArgument : IDisposable /// public Value Value { get; } - private ConstructArgument(string name, object value) + public ConstructArgument(string name, Value value) { Name = name; - Value = Value.From(value); - } - - /// - /// Creates a new construct time parameter, using the given - /// with the given - /// - /// The property to define at the construct time. - /// The property value. - /// The type of the value to set in the property. - /// The type of the value to set in the property. - /// - /// A new instance of , which describe the - /// property-value pair to use at construct time. - /// - public static ConstructArgument With(PropertyDefinition property, T value) where T : notnull - { - return new ConstructArgument(property.UnmanagedName, value); - } - - public static ConstructArgument With(string propertyName, object value) - { - return new ConstructArgument(propertyName, value); + Value = value; } public void Dispose() diff --git a/src/Libs/GObject-2.0/Public/Object.cs b/src/Libs/GObject-2.0/Public/Object.cs index 4a0163e53..cd31430d7 100644 --- a/src/Libs/GObject-2.0/Public/Object.cs +++ b/src/Libs/GObject-2.0/Public/Object.cs @@ -1,7 +1,6 @@ using System; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using GLib; @@ -48,7 +47,7 @@ protected Object(bool owned, ConstructArgument[] constructArguments) objectType: gtype.Value, nProperties: (uint) constructArguments.Length, names: GetNames(constructArguments), - values: GetValues(constructArguments) + values: ValueArray2OwnedHandle.Create(constructArguments.Select(x => x.Value).ToArray()) ); // We can't check if a reference is floating via "g_object_is_floating" here @@ -65,18 +64,6 @@ protected Object(bool owned, ConstructArgument[] constructArguments) private string[] GetNames(ConstructArgument[] constructParameters) => constructParameters.Select(x => x.Name).ToArray(); - private Internal.ValueData[] GetValues(ConstructArgument[] constructParameters) - { - var values = new Internal.ValueData[constructParameters.Length]; - - for (int i = 0; i < constructParameters.Length; i++) - { - values[i] = constructParameters[i].Value.GetData(); - } - - return values; - } - /// /// Does common initialization tasks. /// Wrapper and subclasses can override here to perform immediate initialization. diff --git a/src/Libs/GObject-2.0/Public/SignalArgs.cs b/src/Libs/GObject-2.0/Public/SignalArgs.cs index fd96456da..0f46b20a3 100644 --- a/src/Libs/GObject-2.0/Public/SignalArgs.cs +++ b/src/Libs/GObject-2.0/Public/SignalArgs.cs @@ -18,4 +18,6 @@ internal void SetArgs(Value[] args) { Args = args; } + + protected T Extract(Value value) => value.Extract(); } diff --git a/src/Libs/GObject-2.0/Public/Value.cs b/src/Libs/GObject-2.0/Public/Value.cs index d1806bd25..81ce1575d 100644 --- a/src/Libs/GObject-2.0/Public/Value.cs +++ b/src/Libs/GObject-2.0/Public/Value.cs @@ -5,20 +5,15 @@ namespace GObject; -// TODO: Consider splitting value into different types for each type it represents -// to avoid breaking the open closed principle. -// There could an abstract value base class with generic implementations of the concrete types. public partial class Value : IDisposable { - #region Constructors - public Value(Type type) { - _handle = ValueManagedHandle.Create(); + Handle = ValueManagedHandle.Create(); // We ignore the return parameter as it is a pointer // to the same location like the instance parameter. - _ = Init(_handle, type.Value); + _ = Internal.Value.Init(Handle, type); } public Value(Object value) : this(Type.Object) => SetObject(value); @@ -32,11 +27,15 @@ public Value(Type type) public Value(string value) : this(Type.String) => SetString(value); public Value(string[] value) : this(Type.StringArray) => SetBoxed(Utf8StringArrayNullTerminatedOwnedHandle.Create(value).DangerousGetHandle()); - #endregion - - #region Methods + public Value(Enum value) : this(HasFlags(value) ? Type.Flags : Type.Enum) + { + if (HasFlags(value)) + SetFlags(value); + else + SetEnum(value); + } - internal ValueData GetData() => Marshal.PtrToStructure(Handle.DangerousGetHandle()); + private static bool HasFlags(Enum e) => e.GetType().IsDefined(typeof(FlagsAttribute), false); private nuint GetTypeValue() { @@ -44,81 +43,6 @@ private nuint GetTypeValue() return structure.GType; } - /// - /// Gets an instance of from the given . - /// - /// - /// An instance of if the cast is successful. - /// - /// - /// The given has a type which cannot be parsed as a . - /// - public static Value From(object value) => value switch - { - bool v1 => new Value(v1), - uint v2 => new Value(v2), - int v3 => new Value(v3), - long v4 => new Value(v4), - double v5 => new Value(v5), - float v6 => new Value(v6), - string v7 => new Value(v7), - Enum _ => new Value((long) value), - GLib.Variant v => new Value(v), - Object obj => new Value(obj), - _ => throw new NotSupportedException("Unable to create the value from the given type.") - }; - - public void Set(object? value) - { - switch (value) - { - case bool b: - SetBoolean(b); - break; - case uint u: - SetUint(u); - break; - case int i: - SetInt(i); - break; - case string s: - SetString(s); - break; - case double d: - SetDouble(d); - break; - case Enum e: - if (e.GetType().IsDefined(typeof(FlagsAttribute), false)) - SetFlags(e); - else - SetEnum(e); - break; - case long l: - SetLong(l); - break; - case float f: - SetFloat(f); - break; - case string[] array: - // Marshalling logic happens inside this safe handle. GValue takes a - // copy of the boxed memory so we do not need to keep it alive. The - // Garbage Collector will automatically free the safe handle for us. - var strArray = Utf8StringArrayNullTerminatedOwnedHandle.Create(array); - SetBoxed(strArray.DangerousGetHandle()); - break; - case GLib.Variant v: - SetVariant(v); - break; - case Object o: - SetObject(o); - break; - case null: - break; - default: - throw new NotSupportedException($"Type {value.GetType()} is not supported as a value type"); - } - } - /// /// Extracts the content of this into an object. /// @@ -126,19 +50,19 @@ public void Set(object? value) /// /// The value cannot be casted to the given type. /// - public object? Extract() + internal object? Extract() { var type = GetTypeValue(); return type switch { - (nuint) BasicType.Boolean => GetBool(), + (nuint) BasicType.Boolean => GetBoolean(), (nuint) BasicType.UInt => GetUint(), (nuint) BasicType.Int => GetInt(), (nuint) BasicType.Long => GetLong(), (nuint) BasicType.Double => GetDouble(), (nuint) BasicType.Float => GetFloat(), (nuint) BasicType.String => GetString(), - (nuint) BasicType.Pointer => GetPtr(), + (nuint) BasicType.Pointer => GetPointer(), _ => CheckComplexTypes(type) }; } @@ -168,19 +92,11 @@ public void Set(object? value) throw new NotSupportedException($"Unable to extract the value for type '{name}'. The type (id: {gtype}) is unknown."); } - public T Extract() => (T) Extract()!; - - public IntPtr GetPtr() => Internal.Value.GetPointer(Handle); - - public ParamSpec GetParam() - { - var paramHandle = Internal.Value.GetParam(Handle); - return new ParamSpec(paramHandle); - } + internal T Extract() => (T) Extract()!; public object? GetBoxed(nuint type) { - IntPtr ptr = Internal.Value.GetBoxed(Handle); + var ptr = Internal.Value.GetBoxed(Handle); if (ptr == IntPtr.Zero) return null; @@ -202,62 +118,81 @@ public ParamSpec GetParam() ); } - public Object? GetObject() - => ObjectWrapper.WrapNullableHandle(Internal.Value.GetObject(Handle), false); - - public bool GetBool() => Internal.Value.GetBoolean(Handle); - public uint GetUint() => Internal.Value.GetUint(Handle); - public int GetInt() => Internal.Value.GetInt(Handle); - public long GetLong() => Internal.Value.GetLong(Handle); - public double GetDouble() => Internal.Value.GetDouble(Handle); - public float GetFloat() => Internal.Value.GetFloat(Handle); - public uint GetFlags() => Internal.Value.GetFlags(Handle); - public int GetEnum() => Internal.Value.GetEnum(Handle); - public string? GetString() => GetString(Handle).ConvertToString(); - public GLib.Variant? GetVariant() + public string[]? GetStringArray() { - var result = Internal.Value.GetVariant(Handle); - return result.IsInvalid ? null : new(result.OwnedCopy()); - } + var ptr = Internal.Value.GetBoxed(Handle); - private void SetBoxed(IntPtr ptr) => Internal.Value.SetBoxed(Handle, ptr); - private void SetBoolean(bool b) => Internal.Value.SetBoolean(Handle, b); - private void SetUint(uint u) => Internal.Value.SetUint(Handle, u); - private void SetInt(int i) => Internal.Value.SetInt(Handle, i); - private void SetDouble(double d) => Internal.Value.SetDouble(Handle, d); - private void SetFloat(float f) => Internal.Value.SetFloat(Handle, f); - private void SetLong(long l) => Internal.Value.SetLong(Handle, l); - private void SetEnum(Enum e) => Internal.Value.SetEnum(Handle, Convert.ToInt32(e)); - private void SetFlags(Enum e) => Internal.Value.SetFlags(Handle, Convert.ToUInt32(e)); - private void SetString(string s) => Internal.Value.SetString(Handle, GLib.Internal.NullableUtf8StringOwnedHandle.Create(s)); - private void SetVariant(GLib.Variant v) => Internal.Value.SetVariant(Handle, v.Handle); - private void SetObject(Object o) => Internal.Value.SetObject(Handle, o.Handle); + return ptr == IntPtr.Zero + ? null + : new Utf8StringArrayNullTerminatedUnownedHandle(ptr).ConvertToStringArray(); + } - public void Dispose() + public T GetFlags() where T : Enum { - Handle.Dispose(); + return (T) Enum.ToObject(typeof(T), Internal.Value.GetFlags(Handle)); } - #endregion - - #region Internal + public T GetEnum() where T : Enum + { + return (T) Enum.ToObject(typeof(T), Internal.Value.GetEnum(Handle)); + } - // This redeclares the "g_value_init" method. The internal method - // returns a GObject.Internal.Value.Handle which can not be freed - // via a free function. The Marshaller would create an instance - // of GObject.Internal.Value.Handle and the GC would try to - // dispose it which is not possible and throws an Exception. To - // avoid the GObject.Internal.Value.Handle creation this method - // returns just an IntPtr. It is okay to return an IntPtr as the - // returned IntPtr points to the location of the "value" parameter. - [DllImport(ImportResolver.Library, EntryPoint = "g_value_init")] - private static extern IntPtr Init(GObject.Internal.ValueHandle value, nuint gType); + public void SetEnum(Enum e) => Internal.Value.SetEnum(Handle, Convert.ToInt32(e)); + public void SetFlags(Enum e) => Internal.Value.SetFlags(Handle, Convert.ToUInt32(e)); - //TODO: g_value_get_string get's redeclared here as it is not annotated correctly. - //Remove after release of: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3301 - //Use "Internal.Value.GetString(Handle).ConvertToString();" again - [DllImport(ImportResolver.Library, EntryPoint = "g_value_get_string")] - public static extern GLib.Internal.NullableUtf8StringUnownedHandle GetString(GObject.Internal.ValueHandle value); + internal void Set(object? value) + { + switch (value) + { + case bool b: + SetBoolean(b); + break; + case uint u: + SetUint(u); + break; + case int i: + SetInt(i); + break; + case string s: + SetString(s); + break; + case double d: + SetDouble(d); + break; + case Enum e: + if (HasFlags(e)) + SetFlags(e); + else + SetEnum(e); + break; + case long l: + SetLong(l); + break; + case float f: + SetFloat(f); + break; + case string[] array: + // Marshalling logic happens inside this safe handle. GValue takes a + // copy of the boxed memory so we do not need to keep it alive. The + // Garbage Collector will automatically free the safe handle for us. + var strArray = Utf8StringArrayNullTerminatedOwnedHandle.Create(array); + SetBoxed(strArray.DangerousGetHandle()); + break; + case GLib.Variant v: + SetVariant(v); + break; + case Object o: + SetObject(o); + break; + case null: + break; + default: + throw new NotSupportedException($"Type {value.GetType()} is not supported as a value type"); + } + } - #endregion + public void Dispose() + { + Handle.Dispose(); + } } diff --git a/src/Libs/Gst-1.0/Public/Bus.cs b/src/Libs/Gst-1.0/Public/Bus.cs deleted file mode 100644 index 7e65e9c04..000000000 --- a/src/Libs/Gst-1.0/Public/Bus.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Gst; - -public partial class Bus -{ - //TODO: This method is a shortcut for the user and should probably be part of the toolkit layer - public void WaitForEndOrError() - => TimedPopFiltered(Constants.CLOCK_TIME_NONE); - - //TODO: This method is a shortcut for the user and should probably be part of the toolkit layer - public void TimedPopFiltered(ulong timeout) - { - Internal.Bus.TimedPopFiltered(Handle, timeout, (MessageType.Eos | MessageType.Error)); - } - - /*TODO Enable - public uint AddWatchFull(int priority, BusFunc func) - => AddWatchFull(priority, func, null);*/ - - // public uint AddWatchFull(int priority, BusFunc func, DestroyNotify? notify) - // => throw new NotImplementedException(); //TODO Native.add_watch_full(Handle, priority, func, IntPtr.Zero, notify!); -} diff --git a/src/Libs/Gst-1.0/Public/Element.cs b/src/Libs/Gst-1.0/Public/Element.cs deleted file mode 100644 index b03f60fe2..000000000 --- a/src/Libs/Gst-1.0/Public/Element.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using GLib; -using Value = GObject.Value; - -namespace Gst; - -public partial class Element -{ - #region Fields - - /*public State CurrentState - { - get => GetObjectStruct().current_state; - set - { - Fields fields = GetObjectStruct(); - fields.current_state = value; - SetObjectStruct(fields); - } - }*/ - - // NOTE: Be careful about providing access to fields - // We should (almost) always go through methods, unless - // we have a good reason. This field accessor below caused - // severe memory corruption issues when used. - - // public Bus Bus - // { - // get - // { - // Fields fields = GetObjectStruct(); - // IntPtr bus = fields.bus; - // return WrapHandle(bus); - // // WrapHandle(GetObjectStruct().bus); - // } - // set - // { - // Fields fields = GetObjectStruct(); - // fields.bus = GetHandle(value); - // SetObjectStruct(fields); - // } - // } - - #endregion - - /*public Bus? GetBus() - { - IntPtr ptr = Native.Element.Instance.Methods.GetBus(Handle); - return GObject.Native.ObjectWrapper.WrapNullableHandle(ptr, true); - } - - public bool AddPad(Pad pad) => Native.Element.Instance.Methods.AddPad(Handle, pad.Handle); - - public StateChangeReturn SetState(State state) - => Native.Element.Instance.Methods.SetState(Handle, state); - - public StateChangeReturn GetState(out State state, out State pending, ulong timeout) - { - IntPtr statePtr = IntPtr.Zero; - IntPtr pendingPtr = IntPtr.Zero; - var result = Native.Element.Instance.Methods.GetState(Handle, out statePtr, out pendingPtr, timeout); - - state = Marshal.PtrToStructure(statePtr); - pending = Marshal.PtrToStructure(pendingPtr); - - Marshal.FreeHGlobal(statePtr); - Marshal.FreeHGlobal(pendingPtr); - - return result; - } - - public bool SeekSimple(Format format, SeekFlags seekFlags, long seekPos) - => Native.Element.Instance.Methods.SeekSimple(Handle, format, seekFlags, seekPos); - - public bool QueryPosition(Format format, out long cur) - { - return Native.Element.Instance.Methods.QueryPosition(Handle, format, out cur); - } - - public bool QueryDuration(Format format, out long duration) - { - return Native.Element.Instance.Methods.QueryDuration(Handle, format, out duration); - } - - public Pad? GetStaticPad(string name) - => throw new NotImplementedException(); //TODO WrapNullableHandle(Native.Instance.Methods.GetStaticPad(Handle, name), true); - - public static void Unlink(Element src, Element dest) - => Native.Element.Instance.Methods.Unlink(src.Handle, dest.Handle); - - public void Unlink(Element dest) => Unlink(this, dest); - - public bool Link(Element dest) => Link(this, dest); - - public static bool Link(Element src, Element dest) - => Native.Element.Instance.Methods.Link(src.Handle, dest.Handle); - - // FIXME: This function is the culprit for wavparse0 errors - // TODO: Make this work properly, and additionally clean up - // the API in the process. - public static bool Link(params Element[] elements) - { - // TODO: Should this return false? - if (elements.Length < 2) - return false; - - Element prev = elements[0]; - foreach (var el in elements[1..]) - { - // TODO: Should we try and keep going? - if (!Link(prev, el)) - return false; - - prev = el; - } - - return true; - } - - public Pad? GetRequestPad(string name) - => throw new NotImplementedException(); //TODO WrapNullableHandle(Native.Instance.Methods.GetRequestPad(Handle, name), true); - - public bool SyncStateWithParent() - => Native.Element.Instance.Methods.SyncStateWithParent(Handle);*/ - - // Some older mono applications appear to use a - // string indexer to lookup properties from GLib - // for GStreamer objects, as we do not know plugin - // objects at compile time. - // - // This is a rudimentary implementation of - // a property indexer in order to help port over - // mono/gtk2 applications. - // - // TODO: We likely want to move this into GObject in the long term - // e.g. via custom Property Descriptors - public object? this[string property] - { - get - { - try - { - var handle = GObject.Internal.ValueManagedHandle.Create(); - GObject.Internal.Object.GetProperty(Handle, GLib.Internal.NonNullableUtf8StringOwnedHandle.Create(property), handle); - - return new Value(handle).Extract(); - } - catch (Exception e) - { - throw new Exception($"Property Not Found", e); - } - } - - set - { - try - { - // We intentionally throw an exception if the type of value cannot be wrapped - // TODO: Support boxing arbitrary managed types - // TODO: Move this checking code into GObject proper for - // safer and more reliable access to properties. - Value val; - if (value?.GetType().IsAssignableTo(typeof(GObject.Object)) ?? false) - val = Value.From((Object) value!); - else - val = Value.From(value); - - SetProperty(property, val); - } - catch (Exception e) - { - throw new Exception($"Property Not Found", e); - } - } - } -} diff --git a/src/Libs/Gst-1.0/Public/Global.cs b/src/Libs/Gst-1.0/Public/Global.cs deleted file mode 100644 index 35e83a0d5..000000000 --- a/src/Libs/Gst-1.0/Public/Global.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Gst; - -public static partial class Global -{ - public static uint ResourceErrorQuark - => throw new NotImplementedException(); //TODO Native.resource_error_quark(); - - public static uint StreamErrorQuark - => throw new NotImplementedException(); //TODO Native.stream_error_quark(); - - public static uint LibraryErrorQuark - => throw new NotImplementedException(); //TODO Native.library_error_quark(); - - public static uint CoreErrorQuark - => throw new NotImplementedException(); //TODO Native.core_error_quark(); -} diff --git a/src/Libs/Gst-1.0/Public/Message.cs b/src/Libs/Gst-1.0/Public/Message.cs deleted file mode 100644 index 935b217e0..000000000 --- a/src/Libs/Gst-1.0/Public/Message.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using GLib; - -namespace Gst; - -public class MessageTypeMismatchException : Exception -{ - public MessageTypeMismatchException(MessageType actual, MessageType expected) - : base($"Expected message type of {expected} but received ${actual}.") - { - - } -} - -//TODO -/*public partial record Message -{ - public MessageType Type => type; - - //TODO: Clarify if this is needed, see: https://github.com/gircore/gir.core/pull/184#discussion_r554907963 - public Gst.Object Src - { - get => GObject.Object.WrapHandle(src, false); - set => src = value.Handle; - } - - public Structure GetStructure() - { - // Marshal this structure - IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - IntPtr ptr = Native.get_structure(thisPtr); - - // Update this structure (is this necessary?) - // TODO: Check for NULL - this = Marshal.PtrToStructure(thisPtr)!; - - Marshal.FreeHGlobal(thisPtr); - - return Marshal.PtrToStructure(ptr); - } - - public void ParseStateChanged(out State? oldState, out State? newState, out State? pendingState) - { - if (type != MessageType.StateChanged) - throw new MessageTypeMismatchException(type, MessageType.StateChanged); - - // Empty pointers - IntPtr oldStatePtr = default, newStatePtr = default, pendingStatePtr = default; - - // Marshal this structure - IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - Native.parse_state_changed(thisPtr, out oldStatePtr, out newStatePtr, out pendingStatePtr); - - // Update and free (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!; - Marshal.FreeHGlobal(thisPtr); - - // Assign out variables - oldState = (State) oldStatePtr;// != IntPtr.Zero ? Marshal.PtrToStructure(oldStatePtr) : null; - newState = (State) newStatePtr;// != IntPtr.Zero ? Marshal.PtrToStructure(newStatePtr) : null; - pendingState = (State) pendingStatePtr;// != IntPtr.Zero ? Marshal.PtrToStructure(pendingStatePtr) : null; - } - - public void ParseTag(out TagList? tagList) - { - if (type != MessageType.Tag) - throw new MessageTypeMismatchException(type, MessageType.Tag); - - // Empty pointers - IntPtr tagListPtr = default; - - // Marshal this structure - IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - Native.parse_tag(thisPtr, out tagListPtr); - - // Update and free (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!; - Marshal.FreeHGlobal(thisPtr); - - // Assign out variables - tagList = tagListPtr != IntPtr.Zero ? Marshal.PtrToStructure(tagListPtr) : null; - } - - public void ParseBuffering(out int percent) - { - if (type != MessageType.Buffering) - throw new MessageTypeMismatchException(type, MessageType.Buffering); - - // Empty pointers - percent = 0; - - // Marshal this structure - IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - Native.parse_buffering(thisPtr, out percent); - - // Update and free (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!; - Marshal.FreeHGlobal(thisPtr); - } - - public void ParseError(out GLib.Error? error, out string? debug) - { - if (type != MessageType.Error) - throw new MessageTypeMismatchException(type, MessageType.Error); - - // Empty pointers - - // Marshal this structure - IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - Native.parse_error(thisPtr, out IntPtr errPtr, out IntPtr strPtr); - - // Update and free (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!; - debug = StringHelper.ToNullableAnsiStringAndFree(strPtr); - error = Marshal.PtrToStructure(errPtr); - Marshal.FreeHGlobal(thisPtr); - } -}*/ diff --git a/src/Libs/Gst-1.0/Public/Object.cs b/src/Libs/Gst-1.0/Public/Object.cs deleted file mode 100644 index 4bc5f460a..000000000 --- a/src/Libs/Gst-1.0/Public/Object.cs +++ /dev/null @@ -1,48 +0,0 @@ -using GObject; - -namespace Gst; - -public partial class Object -{ - //TODO - /*public static readonly Property NameProperty = Property.Register( - Native.NameProperty, - nameof(Name), - (o) => o.Name, - (o, v) => o.Name = v - ); - - public string Name - { - get => GetProperty(NameProperty); - set => SetProperty(NameProperty, value); - } - - public void SetName(string name) - => Native.Methods.SetName(Handle, name); - - // TODO: Marshal as enum - public uint Flags - { - get - { - // Safe Version: - Fields fields = GetObjectStruct(); - return fields.flags; - - // Unsafe Version: - // return ((Fields*) Handle)->flags; - } - set - { - // Safe Version: - Fields fields = GetObjectStruct(); - fields.flags = value; - SetObjectStruct(fields); - - // Unsafe Version: - // Fields* ptr = (Fields*) Handle; - // ptr->flags = value; - } - }*/ -} diff --git a/src/Libs/Gst-1.0/Public/Pad.cs b/src/Libs/Gst-1.0/Public/Pad.cs deleted file mode 100644 index f84632ffe..000000000 --- a/src/Libs/Gst-1.0/Public/Pad.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Gst; - -public partial class Pad -{ - #region Fields - - #endregion - - public static PadLinkReturn Link(Pad sourcePad, Pad sinkPad) - => Internal.Pad.Link(sourcePad.Handle, sinkPad.Handle); - - public static bool Unlink(Pad sourcePad, Pad sinkPad) - => Internal.Pad.Unlink(sourcePad.Handle, sinkPad.Handle); - - /*public PadLinkReturn Link(Pad sinkPad) => Link(this, sinkPad); - public bool Unlink(Pad sinkPad) => Unlink(this, sinkPad); - - public bool IsLinked() => Native.Pad.Instance.Methods.IsLinked(Handle); - public bool IsBlocked() => Native.Pad.Instance.Methods.IsBlocked(Handle); - public bool IsBlocking() => Native.Pad.Instance.Methods.IsBlocking(Handle); - public bool IsActive() => Native.Pad.Instance.Methods.IsActive(Handle); - - public Caps? QueryCaps() => QueryCaps(null); - - public Pad? GetPeer() => throw new NotImplementedException(); //TODO WrapNullableHandle(Native.Methods.GetPeer(Handle), true); - - public Element? GetParentElement() => throw new NotImplementedException(); //TODO WrapNullableHandle(Native.Methods.GetParentElement(Handle), true); - - public ulong AddProbe(PadProbeType mask, PadProbeCallback callback, DestroyNotify? notify = null) - { - throw new NotImplementedException(); //TODO - // TODO: Can we use null-forgiving here? Fix generator - //return Native.add_probe(Handle, mask, callback, IntPtr.Zero, notify!); - } - - public Caps? QueryCaps(Caps? filter) - { - IntPtr ptr = IntPtr.Zero; - if (filter != null) - { - ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(filter, ptr, false); - } - - throw new NotImplementedException(); //TODO - //IntPtr ret = Native.Methods.QueryCaps(Handle, ptr); - - //Marshal.FreeHGlobal(ptr); - - // TODO: Should/can this return null? - //return Marshal.PtrToStructure(ret); - }*/ -} diff --git a/src/Libs/Gst-1.0/Public/Structure.cs b/src/Libs/Gst-1.0/Public/Structure.cs deleted file mode 100644 index fac13cf36..000000000 --- a/src/Libs/Gst-1.0/Public/Structure.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Gst; - -public partial class Structure -{ - public string? GetName() - { - throw new NotImplementedException(); //TODO - - // Marshal this structure - /*IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - // Do not free result as ownership is not transferred! - IntPtr result = Native.get_name(thisPtr); - - // TODO: Do we need to update this structure? - // Probably just switch to using ref structs everywhere - // so we don't need to worry about it. - - return Marshal.PtrToStringAnsi(result);*/ - } - - public void SetName(string structureName) - { - throw new NotImplementedException(); //TODO - - // Marshal this structure - /*IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - Native.set_name(thisPtr, structureName); - - // Update this structure (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!;*/ - } -} diff --git a/src/Libs/Gst-1.0/Public/TagList.cs b/src/Libs/Gst-1.0/Public/TagList.cs deleted file mode 100644 index dde5a8ec4..000000000 --- a/src/Libs/Gst-1.0/Public/TagList.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using GObject; - -namespace Gst; - -public partial class TagList -{ - public void Foreach(TagForeachFunc func) - { - throw new NotImplementedException(); //TODO - // Marshal this structure - /*IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - Native.@foreach(thisPtr, func, IntPtr.Zero); - - // Update this structure (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!;*/ - } - - public void Add(TagMergeMode mode, string tag, params Value[] values) - { - foreach (Value val in values) - { - AddValue(mode, tag, val); - } - } - - public void AddValue(TagMergeMode mode, string tag, Value value) - { - throw new NotImplementedException(); //TODO - // Marshal this structure - /*IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - Native.add_value(thisPtr, mode, tag, ref value); - - // Update this structure (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!; - - // Dispose of Value afterwards - value.Dispose();*/ - } - - public uint GetTagSize(string tag) - { - throw new NotImplementedException(); //TODO - // Marshal this structure - /*IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - var result = Native.get_tag_size(thisPtr, tag); - - // Update this structure (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!; - - return result;*/ - } - - public Value GetValueIndex(string tag, uint index) - { - throw new NotImplementedException(); //TODO - // Marshal this structure - /*IntPtr thisPtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(this, thisPtr, false); - - var result = Native.get_value_index(thisPtr, tag, index); - - // Update this structure (TODO: Check for NULL) - this = Marshal.PtrToStructure(thisPtr)!; - - return result;*/ - } -} diff --git a/src/Libs/Gst-1.0/Public/Version.cs b/src/Libs/Gst-1.0/Public/Version.cs deleted file mode 100644 index 227638c27..000000000 --- a/src/Libs/Gst-1.0/Public/Version.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Gst; - -public static class Version -{ - public static int Major => Constants.VERSION_MAJOR; - public static int Minor => Constants.VERSION_MINOR; - public static int Micro => Constants.VERSION_MICRO; - public static int Nano => Constants.VERSION_NANO; - - public static string VersionString - => Internal.Functions.VersionString().ConvertToString(); -} diff --git a/src/Libs/cairo-1.0/Internal/ContextOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/ContextOwnedHandle.cs deleted file mode 100644 index 03eeee7f6..000000000 --- a/src/Libs/cairo-1.0/Internal/ContextOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class ContextOwnedHandle : ContextHandle -{ - protected override partial bool ReleaseHandle() - { - Context.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Internal/DeviceOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/DeviceOwnedHandle.cs deleted file mode 100644 index b6861651b..000000000 --- a/src/Libs/cairo-1.0/Internal/DeviceOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class DeviceOwnedHandle : DeviceHandle -{ - protected override partial bool ReleaseHandle() - { - Device.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Internal/FontFaceOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/FontFaceOwnedHandle.cs deleted file mode 100644 index b7c9d37c6..000000000 --- a/src/Libs/cairo-1.0/Internal/FontFaceOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class FontFaceOwnedHandle : FontFaceHandle -{ - protected override partial bool ReleaseHandle() - { - FontFace.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Internal/FontOptionsOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/FontOptionsOwnedHandle.cs deleted file mode 100644 index 749fd1cb6..000000000 --- a/src/Libs/cairo-1.0/Internal/FontOptionsOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class FontOptionsOwnedHandle : FontOptionsHandle -{ - protected override partial bool ReleaseHandle() - { - FontOptions.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Internal/PatternOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/PatternOwnedHandle.cs deleted file mode 100644 index ffde1b9d2..000000000 --- a/src/Libs/cairo-1.0/Internal/PatternOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class PatternOwnedHandle : PatternHandle -{ - protected override partial bool ReleaseHandle() - { - Pattern.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Internal/RegionOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/RegionOwnedHandle.cs deleted file mode 100644 index 857e8c669..000000000 --- a/src/Libs/cairo-1.0/Internal/RegionOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class RegionOwnedHandle : RegionHandle -{ - protected override partial bool ReleaseHandle() - { - Region.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Internal/ScaledFontOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/ScaledFontOwnedHandle.cs deleted file mode 100644 index 20ea20e73..000000000 --- a/src/Libs/cairo-1.0/Internal/ScaledFontOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class ScaledFontOwnedHandle : ScaledFontHandle -{ - protected override partial bool ReleaseHandle() - { - ScaledFont.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Internal/SurfaceOwnedHandle.cs b/src/Libs/cairo-1.0/Internal/SurfaceOwnedHandle.cs deleted file mode 100644 index d25ca1adf..000000000 --- a/src/Libs/cairo-1.0/Internal/SurfaceOwnedHandle.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Cairo.Internal; - -public partial class SurfaceOwnedHandle : SurfaceHandle -{ - protected override partial bool ReleaseHandle() - { - Surface.Destroy(handle); - return true; - } -} diff --git a/src/Libs/cairo-1.0/Public/Context.Text.cs b/src/Libs/cairo-1.0/Public/Context.Text.cs index ab5ec089d..802cc9eee 100644 --- a/src/Libs/cairo-1.0/Public/Context.Text.cs +++ b/src/Libs/cairo-1.0/Public/Context.Text.cs @@ -29,13 +29,13 @@ public void SetFontFace(FontFace font_face) => Internal.Context.SetFontFace(Handle, font_face.Handle); public FontFace GetFontFace() - => new FontFace(Internal.Context.GetFontFace(Handle)); + => new FontFace(Internal.Context.GetFontFace(Handle).OwnedCopy()); public void SetScaledFont(ScaledFont scaled_font) => Internal.Context.SetScaledFont(Handle, scaled_font.Handle); public ScaledFont GetScaledFont() - => new ScaledFont(Internal.Context.GetScaledFont(Handle)); + => new ScaledFont(Internal.Context.GetScaledFont(Handle).OwnedCopy()); public void ShowText(string text) => Internal.Context.ShowText(Handle, GLib.Internal.NonNullableUtf8StringOwnedHandle.Create(text)); diff --git a/src/Libs/cairo-1.0/Public/Context.cs b/src/Libs/cairo-1.0/Public/Context.cs index b4176579d..687cfb2e9 100644 --- a/src/Libs/cairo-1.0/Public/Context.cs +++ b/src/Libs/cairo-1.0/Public/Context.cs @@ -14,7 +14,7 @@ public Context(Surface target) public void Restore() => Internal.Context.Restore(Handle); public Surface GetTarget() - => new Surface(Internal.Context.GetTarget(Handle)); + => new Surface(Internal.Context.GetTarget(Handle).OwnedCopy()); #region Groups public void PushGroup() @@ -30,7 +30,7 @@ public void PopGroupToSource() => Internal.Context.PopGroupToSource(Handle); public Surface GetGroupTarget() - => new Surface(Internal.Context.GetGroupTarget(Handle)); + => new Surface(Internal.Context.GetGroupTarget(Handle).OwnedCopy()); #endregion #region Source Pattern @@ -47,7 +47,7 @@ public void SetSourceSurface(Surface surface, double x, double y) => Internal.Context.SetSourceSurface(Handle, surface.Handle, x, y); public Pattern GetSource() - => new Pattern(Internal.Context.GetSource(Handle)); + => new Pattern(Internal.Context.GetSource(Handle).OwnedCopy()); #endregion #region Properties diff --git a/src/Libs/cairo-1.0/Public/Gradient.cs b/src/Libs/cairo-1.0/Public/Gradient.cs index e803f33cf..531561bcb 100644 --- a/src/Libs/cairo-1.0/Public/Gradient.cs +++ b/src/Libs/cairo-1.0/Public/Gradient.cs @@ -2,7 +2,7 @@ public class Gradient : Pattern { - protected Gradient(Internal.PatternHandle handle) + protected Gradient(Internal.PatternOwnedHandle handle) : base(handle) { } diff --git a/src/Libs/cairo-1.0/Public/ScaledFont.cs b/src/Libs/cairo-1.0/Public/ScaledFont.cs index 0b861da15..50d97dab1 100644 --- a/src/Libs/cairo-1.0/Public/ScaledFont.cs +++ b/src/Libs/cairo-1.0/Public/ScaledFont.cs @@ -24,7 +24,7 @@ public void TextExtents(string text, out TextExtents extents) => Internal.ScaledFont.TextExtents(Handle, GLib.Internal.NonNullableUtf8StringOwnedHandle.Create(text), out extents); public FontFace GetFontFace() - => new FontFace(Internal.ScaledFont.GetFontFace(Handle)); + => new FontFace(Internal.ScaledFont.GetFontFace(Handle).OwnedCopy()); public void GetFontOptions(FontOptions options) => Internal.ScaledFont.GetFontOptions(Handle, options.Handle); diff --git a/src/Libs/cairo-1.0/Public/SolidPattern.cs b/src/Libs/cairo-1.0/Public/SolidPattern.cs index 1b3b2efcd..41e8719d4 100644 --- a/src/Libs/cairo-1.0/Public/SolidPattern.cs +++ b/src/Libs/cairo-1.0/Public/SolidPattern.cs @@ -2,7 +2,7 @@ public class SolidPattern : Pattern { - private SolidPattern(Internal.PatternHandle handle) + private SolidPattern(Internal.PatternOwnedHandle handle) : base(handle) { } diff --git a/src/Libs/cairo-1.0/Public/Surface.cs b/src/Libs/cairo-1.0/Public/Surface.cs index 225a030de..e6316b46c 100644 --- a/src/Libs/cairo-1.0/Public/Surface.cs +++ b/src/Libs/cairo-1.0/Public/Surface.cs @@ -6,7 +6,7 @@ public partial class Surface private Device? _device; public Content Content => Internal.Surface.GetContent(Handle); - public Device Device => _device ??= new Device(Internal.Surface.GetDevice(Handle)); + public Device? Device => _device ??= GetDevice(); public Status Status => Internal.Surface.Status(Handle); public SurfaceType SurfaceType => Internal.Surface.GetType(Handle); @@ -60,4 +60,13 @@ public Surface CreateForRectangle(double x, double y, double width, double heigh public void MarkDirty(int x, int y, int width, int height) => Internal.Surface.MarkDirtyRectangle(Handle, x, y, width, height); + + private Device? GetDevice() + { + var deviceHandle = Internal.Surface.GetDevice(Handle); + + return deviceHandle.IsInvalid + ? null + : new Device(deviceHandle.OwnedCopy()); + } } diff --git a/src/Libs/cairo-1.0/Public/SurfacePattern.cs b/src/Libs/cairo-1.0/Public/SurfacePattern.cs index a7b7cf06b..2c619d398 100644 --- a/src/Libs/cairo-1.0/Public/SurfacePattern.cs +++ b/src/Libs/cairo-1.0/Public/SurfacePattern.cs @@ -10,6 +10,6 @@ public SurfacePattern(Surface surface) public Surface GetSurface() { Internal.Pattern.GetSurface(Handle, out var surface_handle); - return new Surface(surface_handle); + return new Surface(surface_handle.OwnedCopy()); } } diff --git a/src/Native/GirTestLib/girtest-record-tester.c b/src/Native/GirTestLib/girtest-record-tester.c deleted file mode 100644 index 5a0ff5ef7..000000000 --- a/src/Native/GirTestLib/girtest-record-tester.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "girtest-record-tester.h" - -G_DEFINE_BOXED_TYPE (GirTestRecordTester, girtest_record_tester, girtest_record_tester_ref, girtest_record_tester_unref) - -/** - * girtest_record_tester_new: (constructor) - * - * Returns: (transfer full): a new `GirTestRecordTester` - **/ -GirTestRecordTester * -girtest_record_tester_new () -{ - GirTestRecordTester *result; - result = g_new0 (GirTestRecordTester, 1); - result->ref_count = 1; - return result; -} - -/** - * girtest_record_tester_ref: - * @self: a `GirTestRecordTester` - * - * Increments the reference count on `data`. - * - * Returns: (transfer full): the data. - **/ -GirTestRecordTester * -girtest_record_tester_ref (GirTestRecordTester *self) -{ - g_return_val_if_fail (self != NULL, NULL); - self->ref_count += 1; - return self; -} - -/** - * girtrest_record_tester_unref: - * @data: (transfer full): a `GirTestRecordTester` - * - * Decrements the reference count on `data` and frees the - * data if the reference count is 0. - **/ -void -girtest_record_tester_unref (GirTestRecordTester *self) -{ - g_return_if_fail (self != NULL); - - self->ref_count -= 1; - if (self->ref_count > 0) - return; - - g_free (self); -} diff --git a/src/Native/GirTestLib/girtest-record-tester.h b/src/Native/GirTestLib/girtest-record-tester.h deleted file mode 100644 index 7c5fce0a5..000000000 --- a/src/Native/GirTestLib/girtest-record-tester.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -G_BEGIN_DECLS - -#define GIRTEST_TYPE_RECORD_TESTER (girtest_record_tester_get_type()) - -/** - * GirTestRecordTester: - * - * Just a record. - */ -typedef struct -{ - int ref_count; -} GirTestRecordTester; - -GType girtest_record_tester_get_type (void) G_GNUC_CONST; - -GirTestRecordTester * girtest_record_tester_new (); -GirTestRecordTester * girtest_record_tester_ref (GirTestRecordTester *self); -void girtest_record_tester_unref(GirTestRecordTester *self); - -G_END_DECLS diff --git a/src/Native/GirTestLib/girtest-typed-record-tester.c b/src/Native/GirTestLib/girtest-typed-record-tester.c new file mode 100644 index 000000000..a3261bf9b --- /dev/null +++ b/src/Native/GirTestLib/girtest-typed-record-tester.c @@ -0,0 +1,346 @@ +#include "girtest-typed-record-tester.h" + +G_DEFINE_BOXED_TYPE (GirTestTypedRecordTester, girtest_typed_record_tester, girtest_typed_record_tester_ref, girtest_typed_record_tester_unref) + +/** + * girtest_typed_record_tester_new: (constructor) + * + * Returns: (transfer full): a new `GirTestTypedRecordTester` + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_new () +{ + GirTestTypedRecordTester *result; + result = g_new0 (GirTestTypedRecordTester, 1); + result->ref_count = 1; + return result; +} + +/** + * girtest_typed_record_tester_try_new: + * @returnNull: TRUE to return null, FALSE to create a new instance. + * + * Returns: (transfer full) (nullable): a new `GirTestTypedRecordTester` or NULL + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_try_new (gboolean returnNull) +{ + if(returnNull) + return NULL; + + return girtest_typed_record_tester_new(); +} + +/** + * girtest_typed_record_tester_ref: + * @self: a `GirTestRecordTester` + * + * Increments the reference count on `data`. + * + * Returns: (transfer full): the data. + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_ref (GirTestTypedRecordTester *self) +{ + g_return_val_if_fail (self != NULL, NULL); + self->ref_count += 1; + return self; +} + +/** + * girtest_typed_record_tester_try_ref: + * @self: a `GirTestRecordTester` + * @returnNull: TRUE to return NULL, otherwise FALSE + * + * Increments the reference count on `data`. + * + * Returns: (transfer full) (nullable): the data or NULL + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_try_ref (GirTestTypedRecordTester *self, gboolean returnNull) +{ + if(returnNull) + return NULL; + + return girtest_typed_record_tester_ref(self); +} + +/** + * girtest_typed_record_tester_mirror: + * @data: a `GirTestRecordTester` + * + * Mirrors the given data as the return value. Ownership is not transferred. + * + * Returns: (transfer none): the mirrored data. + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_mirror(GirTestTypedRecordTester *data) +{ + return data; +} + +/** + * girtest_typed_record_tester_nullable_mirror: + * @data: a `GirTestRecordTester` + * @mirror: true to mirror data, false to return NULL + * + * Mirrors the given data as the return value if @mirror is true. Ownership is not transferred. + * + * Returns: (transfer none) (nullable): the mirrored data or NULL. + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_nullable_mirror(GirTestTypedRecordTester *data, gboolean mirror) +{ + if(!mirror) + return NULL; + + return data; +} + +/** + * girtrest_typed_record_tester_unref: + * @self: (transfer full): a `GirTestTypedRecordTester` + * + * Decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_unref (GirTestTypedRecordTester *self) +{ + g_return_if_fail (self != NULL); + + self->ref_count -= 1; + if (self->ref_count > 0) + return; + + g_free (self); +} + +/** + * girtest_typed_record_tester_get_ref_count: + * @self: a `GirTestTypedRecordTester` + * + * Returns: The current ref count of the record. + **/ +int +girtest_typed_record_tester_get_ref_count(GirTestTypedRecordTester *self) +{ + g_return_val_if_fail (self != NULL, -1); + return self->ref_count; +} + +/** + * girtest_typed_record_tester_try_get_ref_count: + * @dummy: not used + * @self: (nullable): a `GirTestTypedRecordTester` + * + * Returns: The current ref count of the record or -1 if @self is NULL + **/ +int girtest_typed_record_tester_try_get_ref_count(int dummy, GirTestTypedRecordTester *self) +{ + if(self == NULL) + return -1; + + return self->ref_count; +} + +/** + * girtest_typed_record_tester_take_and_unref: + * @self: (transfer full): a `GirTestTypedRecordTester` + * + * Takes ownership and decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_take_and_unref(GirTestTypedRecordTester *self) +{ + girtest_typed_record_tester_unref(self); +} + +/** + * girtest_typed_record_tester_take_and_unref_func: + * @dummy: Just an unused dummy value + * @data: (transfer full): a `GirTestTypedRecordTester` + * + * Takes ownership and decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_take_and_unref_func(int dummy, GirTestTypedRecordTester *data) +{ + girtest_typed_record_tester_take_and_unref(data); +} + +/** + * girtest_typed_record_tester_take_and_unref_func_nullable: + * @dummy: Just an unused dummy value + * @data: (transfer full) (nullable): a `GirTestTypedRecordTester` + * + * Takes ownership and decrements the reference count on `data` and frees the + * data if the reference count is 0. + **/ +void +girtest_typed_record_tester_take_and_unref_func_nullable(int dummy, GirTestTypedRecordTester *data) +{ + if(data == NULL) + return; + + girtest_typed_record_tester_take_and_unref(data); +} + +/** + * girtest_typed_record_tester_get_ref_count_sum: + * @data: (array length=size): an array of `GirTestTypedRecordTester` pointers + * @size: The length of @data + * + * Returns: The count of all refs of the @data. + **/ +int girtest_typed_record_tester_get_ref_count_sum(GirTestTypedRecordTester * const *data, gsize size) +{ + int sum = 0; + + for (int i = 0; i < size; i++) + { + sum = sum + girtest_typed_record_tester_get_ref_count(data[i]); + } + + return sum; +} + +/** + * girtest_typed_record_tester_get_ref_count_sum_nullable: + * @data: (nullable) (array length=size): an array of `GirTestTypedRecordTester` pointers + * @size: The length of @data + * + * Returns: The count of all refs of the @data. -1 if NULL is supplied as @data. + **/ +int girtest_typed_record_tester_get_ref_count_sum_nullable(GirTestTypedRecordTester * const *data, gsize size) +{ + if(data == NULL) + return -1; + + return girtest_typed_record_tester_get_ref_count_sum(data, size); +} + +/** + * girtest_typed_record_tester_run_callback_return_no_ownership_transfer: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance. + * + * Returns: (transfer none): a GirTestTypedRecordTester + **/ +GirTestTypedRecordTester * +girtest_typed_record_tester_run_callback_return_no_ownership_transfer(GirTestCreateTypedRecordTesterNoOwnershipTransfer callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_return_no_ownership_transfer_nullable: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance or NULL + * + * Returns: (transfer none) (nullable): a GirTestTypedRecordTester + **/ +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_no_ownership_transfer_nullable(GirTestCreateTypedRecordTesterNoOwnershipTransferNullable callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_return_full_ownership_transfer: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance. + * + * Returns: (transfer full): a GirTestTypedRecordTester + **/ +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer(GirTestCreateTypedRecordTesterFullOwnershipTransfer callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_return_full_ownership_transfer_nullable: + * @callback: (scope call): a callback + * + * Calls the callback and returns the newly created instance. + * + * Returns: (transfer full) (nullable): a GirTestTypedRecordTester or NULL + **/ +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer_nullable(GirTestCreateTypedRecordTesterFullOwnershipTransferNullable callback) +{ + return callback(); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer: + * @callback: (scope call): a callback + * + * Calls the callback and supplies a new TypedRecordTester. + **/ +void +girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer(GirTestGetTypedRecordTesterFullOwnershipTransfer callback) +{ + callback(girtest_typed_record_tester_new()); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer_nullable: + * @useNull: TRUE to pass null to the callback, otherwise FALSE. + * @callback: (scope call): a callback + * + * Calls the callback and supplies a new TypedRecordTester if @useNull is FALSE. + **/ +void girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer_nullable(gboolean useNull, GirTestGetTypedRecordTesterFullOwnershipTransferNullable callback) +{ + if(useNull) + callback(NULL); + else + callback(girtest_typed_record_tester_new()); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer: + * @callback: (scope call): a callback + * @data: (transfer none): A GirTestTypedRecordTester + * + * Calls the callback and supplies the given TypedRecordTester. + **/ +void +girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer(GirTestGetTypedRecordTesterNoOwnershipTransfer callback, GirTestTypedRecordTester *data) +{ + callback(data); +} + +/** + * girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer_nullable: + * @callback: (scope call): a callback + * @data: (transfer none) (nullable): A GirTestTypedRecordTester + * + * Calls the callback and supplies the given TypedRecordTester. + **/ +void +girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer_nullable(GirTestGetTypedRecordTesterNoOwnershipTransferNullable callback, GirTestTypedRecordTester *data) +{ + callback(data); +} + +/** + * girtest_typed_record_tester_run_callback_create_nullable_full_ownership_transfer_out: + * @callback: (scope call): a callback + * + * Calls the callback and returns the output parameter of the callback + * + * Returns: (transfer full) (nullable): a GirTestTypedRecordTester + **/ +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_create_nullable_full_ownership_transfer_out(GirTestCreateNullableTypedRecordTesterFullOwnershipTransferInCallback callback) +{ + GirTestTypedRecordTester **ptr = &((GirTestTypedRecordTester *){0}); + + callback(ptr); + + return *ptr; +} \ No newline at end of file diff --git a/src/Native/GirTestLib/girtest-typed-record-tester.h b/src/Native/GirTestLib/girtest-typed-record-tester.h new file mode 100644 index 000000000..b8a8bb5c0 --- /dev/null +++ b/src/Native/GirTestLib/girtest-typed-record-tester.h @@ -0,0 +1,103 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +/** + * GirTestTypedRecordTester: + * + * Just a record. + */ +struct _GirTestTypedRecordTester +{ + int ref_count; +}; + +typedef struct _GirTestTypedRecordTester GirTestTypedRecordTester; +#define GIRTEST_TYPE_TYPED_RECORD_TESTER (girtest_typed_record_tester_get_type()) + +GType girtest_typed_record_tester_get_type (void) G_GNUC_CONST; + +/** + * GirTestCreateTypedRecordTesterNoOwnershipTransfer: + * + * Returns: (transfer none): a new OpaqueRecordTester. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterNoOwnershipTransfer) (); + +/** + * GirTestCreateTypedRecordTesterNoOwnershipTransferNullable: + * + * Returns: (transfer none) (nullable): a new OpaqueRecordTester or NULL. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterNoOwnershipTransferNullable) (); + +/** + * GirTestCreateTypedRecordTesterFullOwnershipTransfer: + * + * Returns: (transfer full): a new TypedRecordTester. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterFullOwnershipTransfer) (); + +/** + * GirTestCreateTypedRecordTesterFullOwnershipTransferNullable: + * + * Returns: (transfer full) (nullable): a new TypedRecordTester or NULL. + */ +typedef GirTestTypedRecordTester* (*GirTestCreateTypedRecordTesterFullOwnershipTransferNullable) (); + +/** + * GirTestGetTypedRecordTesterFullOwnershipTransfer: + * @data: (transfer full): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterFullOwnershipTransfer) (GirTestTypedRecordTester *data); + +/** + * GirTestGetTypedRecordTesterFullOwnershipTransferNullable: + * @data: (transfer full) (nullable): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterFullOwnershipTransferNullable) (GirTestTypedRecordTester *data); + +/** + * GirTestGetTypedRecordTesterNoOwnershipTransfer: + * @data: (transfer none): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterNoOwnershipTransfer) (GirTestTypedRecordTester *data); + +/** + * GirTestGetTypedRecordTesterNoOwnershipTransferNullable: + * @data: (transfer none) (nullable): An TypedRecordTester + */ +typedef void (*GirTestGetTypedRecordTesterNoOwnershipTransferNullable) (GirTestTypedRecordTester *data); + +/** + * GirTestCreateNullableTypedRecordTesterFullOwnershipTransferInCallback: + * @data: (transfer full) (nullable) (out): A TypedRecordTester + */ +typedef void (*GirTestCreateNullableTypedRecordTesterFullOwnershipTransferInCallback) (GirTestTypedRecordTester **data); + +GirTestTypedRecordTester * girtest_typed_record_tester_new (); +GirTestTypedRecordTester * girtest_typed_record_tester_try_new (gboolean returnNull); +GirTestTypedRecordTester * girtest_typed_record_tester_ref (GirTestTypedRecordTester *self); +GirTestTypedRecordTester * girtest_typed_record_tester_try_ref (GirTestTypedRecordTester *self, gboolean returnNull); +GirTestTypedRecordTester * girtest_typed_record_tester_mirror(GirTestTypedRecordTester *data); +GirTestTypedRecordTester * girtest_typed_record_tester_nullable_mirror(GirTestTypedRecordTester *data, gboolean mirror); +void girtest_typed_record_tester_unref(GirTestTypedRecordTester *self); +int girtest_typed_record_tester_get_ref_count(GirTestTypedRecordTester *self); +int girtest_typed_record_tester_try_get_ref_count(int dummy, GirTestTypedRecordTester *self); +void girtest_typed_record_tester_take_and_unref(GirTestTypedRecordTester *self); +void girtest_typed_record_tester_take_and_unref_func(int dummy, GirTestTypedRecordTester *data); +void girtest_typed_record_tester_take_and_unref_func_nullable(int dummy, GirTestTypedRecordTester *data); +int girtest_typed_record_tester_get_ref_count_sum(GirTestTypedRecordTester * const *data, gsize size); +int girtest_typed_record_tester_get_ref_count_sum_nullable(GirTestTypedRecordTester * const *data, gsize size); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_no_ownership_transfer(GirTestCreateTypedRecordTesterNoOwnershipTransfer callback); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_no_ownership_transfer_nullable(GirTestCreateTypedRecordTesterNoOwnershipTransferNullable callback); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer(GirTestCreateTypedRecordTesterFullOwnershipTransfer callback); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_return_full_ownership_transfer_nullable(GirTestCreateTypedRecordTesterFullOwnershipTransferNullable callback); +void girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer(GirTestGetTypedRecordTesterFullOwnershipTransfer callback); +void girtest_typed_record_tester_run_callback_parameter_full_ownership_transfer_nullable(gboolean useNull, GirTestGetTypedRecordTesterFullOwnershipTransferNullable callback); +void girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer(GirTestGetTypedRecordTesterNoOwnershipTransfer callback, GirTestTypedRecordTester *data); +void girtest_typed_record_tester_run_callback_parameter_no_ownership_transfer_nullable(GirTestGetTypedRecordTesterNoOwnershipTransferNullable callback, GirTestTypedRecordTester *data); +GirTestTypedRecordTester * girtest_typed_record_tester_run_callback_create_nullable_full_ownership_transfer_out(GirTestCreateNullableTypedRecordTesterFullOwnershipTransferInCallback callback); +G_END_DECLS diff --git a/src/Native/GirTestLib/girtest.h b/src/Native/GirTestLib/girtest.h index 546b2b76b..b00bd5c5f 100644 --- a/src/Native/GirTestLib/girtest.h +++ b/src/Native/GirTestLib/girtest.h @@ -15,11 +15,11 @@ #include "girtest-platform-string-array-null-terminated-tester.h" #include "girtest-primitive-value-type-tester.h" #include "girtest-property-tester.h" -#include "girtest-record-tester.h" #include "girtest-rename-to-tester.h" #include "girtest-returning-signal-tester.h" #include "girtest-signal-tester.h" #include "girtest-string-tester.h" +#include "girtest-typed-record-tester.h" #include "girtest-utf8-string-array-null-terminated-tester.h" #include "data/girtest-executor.h" #include "data/girtest-executor-impl.h" diff --git a/src/Native/GirTestLib/meson.build b/src/Native/GirTestLib/meson.build index f76267d15..6f79923eb 100644 --- a/src/Native/GirTestLib/meson.build +++ b/src/Native/GirTestLib/meson.build @@ -18,11 +18,11 @@ header_files = [ 'girtest-platform-string-array-null-terminated-tester.h', 'girtest-primitive-value-type-tester.h', 'girtest-property-tester.h', - 'girtest-record-tester.h', 'girtest-rename-to-tester.h', 'girtest-returning-signal-tester.h', 'girtest-signal-tester.h', 'girtest-string-tester.h', + 'girtest-typed-record-tester.h', 'girtest-utf8-string-array-null-terminated-tester.h', 'data/girtest-executor.h', 'data/girtest-executor-impl.h', @@ -42,11 +42,11 @@ source_files = [ 'girtest-platform-string-array-null-terminated-tester.c', 'girtest-primitive-value-type-tester.c', 'girtest-property-tester.c', - 'girtest-record-tester.c', 'girtest-rename-to-tester.c', 'girtest-returning-signal-tester.c', 'girtest-signal-tester.c', 'girtest-string-tester.c', + 'girtest-typed-record-tester.c', 'girtest-utf8-string-array-null-terminated-tester.c', 'data/girtest-executor.c', 'data/girtest-executor-impl.c', diff --git a/src/Samples/Gst-1.0/VideoPlayback/Play.cs b/src/Samples/Gst-1.0/VideoPlayback/Play.cs index 16a5fcb4f..3bb805896 100644 --- a/src/Samples/Gst-1.0/VideoPlayback/Play.cs +++ b/src/Samples/Gst-1.0/VideoPlayback/Play.cs @@ -13,7 +13,7 @@ public static void Play() Element ret = Functions.ParseLaunch("playbin uri=playbin uri=http://ftp.halifax.rwth-aachen.de/blender/demo/movies/ToS/tears_of_steel_720p.mov"); ret.SetState(State.Playing); Bus bus = ret.GetBus(); - bus.WaitForEndOrError(); + bus.TimedPopFiltered(Constants.CLOCK_TIME_NONE, MessageType.Eos | MessageType.Error); ret.SetState(State.Null); } } diff --git a/src/Tests/Libs/Cairo-1.0.Tests/ImageSurfaceTest.cs b/src/Tests/Libs/Cairo-1.0.Tests/ImageSurfaceTest.cs index f68b42e26..68f64e99e 100644 --- a/src/Tests/Libs/Cairo-1.0.Tests/ImageSurfaceTest.cs +++ b/src/Tests/Libs/Cairo-1.0.Tests/ImageSurfaceTest.cs @@ -40,8 +40,6 @@ public void BindingsShouldSucceed() surf.Content.Should().Be(Content.ColorAlpha); surf.SurfaceType.Should().Be(SurfaceType.Image); - // Just verify this succeeds, until Cairo.Device has more methods - surf.Device.Should().NotBeNull(); var opts = new FontOptions(); surf.GetFontOptions(opts); diff --git a/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs b/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs index bec13e5f1..a4a275d52 100644 --- a/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs +++ b/src/Tests/Libs/GObject-2.0.Tests/Records/ValueTest.cs @@ -1,5 +1,7 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; using FluentAssertions; +using GObject.Internal; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace GObject.Tests; @@ -7,17 +9,81 @@ namespace GObject.Tests; [TestClass, TestCategory("UnitTest")] public class ValueTest : Test { + private static void EnsureBasicType(Value v, Internal.BasicType basicType) + { + var data = Marshal.PtrToStructure(v.Handle.DangerousGetHandle()); + data.GType.Should().Be((nuint) basicType); + Internal.Functions.TypeCheckValue(v.Handle).Should().Be(true); + Internal.Functions.TypeCheckValueHolds(v.Handle, data.GType).Should().BeTrue(); + Internal.Functions.TypeCheckValueHolds(v.Handle, (nuint) basicType).Should().BeTrue(); + } + + [DataTestMethod] + [DataRow(0)] + [DataRow(1)] + [DataRow(10)] + [DataRow(-10)] + public void SupportsInt(int value) + { + var v = new Value(value); + v.GetInt().Should().Be(value); + + EnsureBasicType(v, BasicType.Int); + } + [DataTestMethod] - [DataRow(5)] [DataRow(true)] - [DataRow("TestString")] - [DataRow(7u)] + [DataRow(false)] + public void SupportsBool(bool value) + { + var v = new Value(value); + v.GetBoolean().Should().Be(value); + + EnsureBasicType(v, BasicType.Boolean); + } + + [DataTestMethod] [DataRow(2.0)] + [DataRow(-2.0)] + public void SupportsDouble(double value) + { + var v = new Value(value); + v.GetDouble().Should().Be(value); + + EnsureBasicType(v, BasicType.Double); + } + + [DataTestMethod] [DataRow(2.0f)] - public void ValueFromDataShouldContainGivenData(object data) + [DataRow(-2.0f)] + public void SupportsFloat(float value) + { + var v = new Value(value); + v.GetFloat().Should().Be(value); + + EnsureBasicType(v, BasicType.Float); + } + + [DataTestMethod] + [DataRow(7u)] + [DataRow(1000u)] + public void SupportsLong(long value) + { + var v = new Value(value); + v.GetLong().Should().Be(value); + + EnsureBasicType(v, BasicType.Long); + } + + [DataTestMethod] + [DataRow("ABC")] + [DataRow("")] + public void SupportsString(string value) { - var v = Value.From(data); - v.Extract().Should().Be(data); + var v = new Value(value); + v.GetString().Should().Be(value); + + EnsureBasicType(v, BasicType.String); } [TestMethod] @@ -25,28 +91,10 @@ public void VariantFromDataShouldContainGivenData() { var text = "foo"; var variant = GLib.Variant.NewString(text); - var v = Value.From(variant); + var v = new Value(variant); v.Extract().GetString(out _).Should().Be(text); } - [DataTestMethod] - [DataRow("Hello", Internal.BasicType.String)] - [DataRow(true, Internal.BasicType.Boolean)] - [DataRow(1.5, Internal.BasicType.Double)] - [DataRow(1.5f, Internal.BasicType.Float)] - [DataRow(-7, Internal.BasicType.Int)] - [DataRow(7u, Internal.BasicType.UInt)] - [DataRow(77L, Internal.BasicType.Long)] - public void ValueContainsExpectedBasicType(object data, Internal.BasicType basicType) - { - var v = Value.From(data); - Internal.ValueData str = Marshal.PtrToStructure(v.Handle.DangerousGetHandle()); - str.GType.Should().Be((nuint) basicType); - Internal.Functions.TypeCheckValue(v.Handle).Should().Be(true); - Internal.Functions.TypeCheckValueHolds(v.Handle, str.GType).Should().BeTrue(); - Internal.Functions.TypeCheckValueHolds(v.Handle, (nuint) basicType).Should().BeTrue(); - } - [TestMethod] public void CanSetStringArry() { @@ -55,6 +103,8 @@ public void CanSetStringArry() var result = v.Extract(); result.Should().ContainInOrder(array); + + v.GetStringArray().Should().ContainInOrder(array); } [TestMethod] @@ -66,7 +116,7 @@ public void DisposeShouldFreeUnmanagedMemory() Assert.Inconclusive(); var value = 1; - var v = Value.From(value); + var v = new Value(value); var ptr = v.Handle.DangerousGetHandle(); var d1 = Marshal.PtrToStructure(ptr); diff --git a/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs b/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs index c434a2a1e..10464d305 100644 --- a/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs +++ b/src/Tests/Libs/GirTest-0.1.Tests/BitfieldTest.cs @@ -21,9 +21,15 @@ public void CanBeUsedInGValue() { var flags = BitfieldTesterSimpleFlags.One | BitfieldTesterSimpleFlags.Two; var value = new Value(Type.Flags); - value.Set(flags); - var result = value.Extract(); - result.Should().Be(flags); + value.SetFlags(flags); + + var result1 = value.Extract(); + result1.Should().Be(flags); + + var result2 = value.GetFlags(); + result2.Should().Be(flags); + + value.GetFlags().Should().Be((uint) flags); } [TestMethod] diff --git a/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs b/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs index 7900607c8..ff3d65fe1 100644 --- a/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs +++ b/src/Tests/Libs/GirTest-0.1.Tests/EnumerationTest.cs @@ -18,6 +18,7 @@ public void CanUseMaxInGValue() value.Extract().Should().Be(EnumTesterSimpleEnum.Max); value.GetEnum().Should().Be(0x7FFFFFFF); + value.GetEnum().Should().Be(EnumTesterSimpleEnum.Max); } [TestMethod] @@ -31,5 +32,6 @@ public void CanUseMinInGValue() value.Extract().Should().Be(EnumTesterSimpleEnum.Min); value.GetEnum().Should().Be(1 << 31); + value.GetEnum().Should().Be(EnumTesterSimpleEnum.Min); } } diff --git a/src/Tests/Libs/GirTest-0.1.Tests/TypedRecordTest.cs b/src/Tests/Libs/GirTest-0.1.Tests/TypedRecordTest.cs new file mode 100644 index 000000000..22891d5e9 --- /dev/null +++ b/src/Tests/Libs/GirTest-0.1.Tests/TypedRecordTest.cs @@ -0,0 +1,320 @@ +using System; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace GirTest.Tests; + +[TestClass, TestCategory("BindingTest")] +public class TypedRecordTest : Test +{ + [TestMethod] + public void SupportsConstructorTransferFull() + { + var recordTester = TypedRecordTester.New(); + recordTester.Handle.DangerousGetHandle().Should().NotBe(IntPtr.Zero); + } + + [TestMethod] + public void SupportsConstructorNullableTransferFull() + { + var recordTester = TypedRecordTester.TryNew(false); + recordTester!.Handle.DangerousGetHandle().Should().NotBe(IntPtr.Zero); + + var recordTester2 = TypedRecordTester.TryNew(true); + recordTester2.Should().BeNull(); + } + + [TestMethod] + public void SupportsReturnValueTransferFull() + { + var recordTester = TypedRecordTester.New(); + var recordTester2 = recordTester.Ref(); + recordTester2.GetRefCount().Should().Be(2); + } + + [TestMethod] + public void SupportsReturnValueTransferNone() + { + var recordTester = TypedRecordTester.New(); + var recordTester2 = recordTester.Mirror(); + + recordTester.GetRefCount().Should().Be(2); + recordTester2.GetRefCount().Should().Be(2); + + recordTester.Handle.DangerousGetHandle().Should().Be(recordTester2.Handle.DangerousGetHandle()); + } + + [TestMethod] + public void SupportsReturnValueNullableTransferNone() + { + var recordTester = TypedRecordTester.New(); + var recordTester2 = recordTester.NullableMirror(true); + + recordTester.GetRefCount().Should().Be(2); + recordTester2!.GetRefCount().Should().Be(2); + + recordTester.Handle.DangerousGetHandle().Should().Be(recordTester2.Handle.DangerousGetHandle()); + + var recordTester3 = recordTester.NullableMirror(false); + recordTester3.Should().BeNull(); + } + + [TestMethod] + public void SupportsReturnValueNullableTransferFull() + { + var recordTester = TypedRecordTester.New(); + var recordTester2 = recordTester.TryRef(false); + + recordTester.GetRefCount().Should().Be(2); + recordTester2!.GetRefCount().Should().Be(2); + + recordTester.Handle.DangerousGetHandle().Should().Be(recordTester2.Handle.DangerousGetHandle()); + + var recordTester3 = recordTester.TryRef(true); + recordTester3.Should().BeNull(); + } + + [TestMethod] + public void SupportsInstanceParameterTransferNone() + { + var recordTester = TypedRecordTester.New(); + recordTester.GetRefCount().Should().Be(1); + } + + [TestMethod] + public void SupportsInstanceParameterTransferFull() + { + var recordTester = TypedRecordTester.New(); + recordTester.GetRefCount().Should().Be(1); + recordTester.TakeAndUnref(); + recordTester.GetRefCount().Should().Be(1); + } + + [TestMethod] + public void SupportsParameterTransferFull() + { + var recordTester = TypedRecordTester.New(); + recordTester.GetRefCount().Should().Be(1); + TypedRecordTester.TakeAndUnrefFunc(0, recordTester); + recordTester.GetRefCount().Should().Be(1); + } + + [TestMethod] + public void SupportsParameterNullableTransferFull() + { + var recordTester = TypedRecordTester.New(); + recordTester.GetRefCount().Should().Be(1); + TypedRecordTester.TakeAndUnrefFuncNullable(0, recordTester); + recordTester.GetRefCount().Should().Be(1); + + TypedRecordTester.TakeAndUnrefFuncNullable(0, null); + } + + [TestMethod] + public void SupportsParameterNullableTransferNone() + { + var result = TypedRecordTester.TryGetRefCount(0, null); + result.Should().Be(-1); + + result = TypedRecordTester.TryGetRefCount(0, TypedRecordTester.New()); + result.Should().Be(1); + } + + [TestMethod] + public void SupportsParameterArrayWithLengthParameter() + { + var recordTester1 = TypedRecordTester.New(); + var recordTester2 = TypedRecordTester.New(); + + var result = TypedRecordTester.GetRefCountSum(new[] { recordTester1, recordTester2 }); + result.Should().Be(2); + + TypedRecordTester.GetRefCountSum(Array.Empty()).Should().Be(0); + } + + [TestMethod] + public void SupportsParameterNullableArrayWithLengthParameter() + { + var recordTester1 = TypedRecordTester.New(); + var recordTester2 = TypedRecordTester.New(); + + var result = TypedRecordTester.GetRefCountSumNullable(new[] { recordTester1, recordTester2 }); + result.Should().Be(2); + + TypedRecordTester.GetRefCountSumNullable(Array.Empty()).Should().Be(0); + TypedRecordTester.GetRefCountSumNullable(null).Should().Be(-1); + } + + [TestMethod] + public void SupportsCallbackReturnNoOwnershipTransfer() + { + var recordTester = TypedRecordTester.New(); + + TypedRecordTester Create() + { + return recordTester; + } + + var recordTester2 = TypedRecordTester.RunCallbackReturnNoOwnershipTransfer(Create); + + recordTester.GetRefCount().Should().Be(2); + recordTester2.GetRefCount().Should().Be(2); + recordTester.Handle.DangerousGetHandle().Should().Be(recordTester2.Handle.DangerousGetHandle()); + } + + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void SupportsCallbackReturnNoOwnershipTransferNullable(bool useNull) + { + TypedRecordTester? Create() + { + return useNull ? null : TypedRecordTester.New(); + } + + var recordTester2 = TypedRecordTester.RunCallbackReturnNoOwnershipTransferNullable(Create); + if (useNull) + recordTester2.Should().BeNull(); + else + recordTester2.Should().NotBeNull(); + } + + [TestMethod] + public void SupportsCallbackReturnFullOwnershipTransfer() + { + var recordTester = TypedRecordTester.New(); + + TypedRecordTester Create() + { + return recordTester; + } + + var recordTester2 = TypedRecordTester.RunCallbackReturnFullOwnershipTransfer(Create); + + recordTester.GetRefCount().Should().Be(2); + recordTester2.GetRefCount().Should().Be(2); + recordTester.Handle.DangerousGetHandle().Should().Be(recordTester2.Handle.DangerousGetHandle()); + } + + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void SupportsCallbackReturnFullOwnershipTransferNullable(bool useNull) + { + TypedRecordTester? Create() + { + return useNull ? null : TypedRecordTester.New(); + } + + var recordTester2 = TypedRecordTester.RunCallbackReturnFullOwnershipTransferNullable(Create); + + if (useNull) + recordTester2.Should().BeNull(); + else + recordTester2.Should().NotBeNull(); + } + + [TestMethod] + public void SupportsCallbackParameterFullOwnershipTransfer() + { + var called = false; + + void Callback(TypedRecordTester recordTester) + { + recordTester.GetRefCount().Should().Be(1); + called = true; + } + + TypedRecordTester.RunCallbackParameterFullOwnershipTransfer(Callback); + + called.Should().BeTrue(); + } + + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void SupportsCallbackParameterFullOwnershipTransferNullable(bool useNull) + { + var called = false; + + void Callback(TypedRecordTester? recordTester) + { + (recordTester is null).Should().Be(useNull); + called = true; + } + + TypedRecordTester.RunCallbackParameterFullOwnershipTransferNullable(useNull, Callback); + called.Should().BeTrue(); + } + + [TestMethod] + public void SupportsCallbackParameterNoOwnershipTransfer() + { + var recordTester = TypedRecordTester.New(); + var called = false; + + void Callback(TypedRecordTester obj) + { + obj.GetRefCount().Should().Be(2); + obj.Handle.DangerousGetHandle().Should().Be(recordTester.Handle.DangerousGetHandle()); + called = true; + } + + TypedRecordTester.RunCallbackParameterNoOwnershipTransfer(Callback, recordTester); + + called.Should().BeTrue(); + } + + [DataTestMethod] + [DataRow(true)] + [DataRow(false)] + public void SupportsCallbackParameterNoOwnershipTransferNullable(bool useNull) + { + var recordTester = useNull ? null : TypedRecordTester.New(); + var called = false; + + void Callback(TypedRecordTester? obj) + { + (obj is null).Should().Be(useNull); + + if (!useNull) + obj!.GetRefCount().Should().Be(2); + + called = true; + } + + TypedRecordTester.RunCallbackParameterNoOwnershipTransferNullable(Callback, recordTester); + + called.Should().BeTrue(); + } + + [TestMethod] + public void SupportsWrapHandle() + { + var recordTester = TypedRecordTester.New(); + + var wrapped = (TypedRecordTester) GObject.Internal.BoxedWrapper.WrapHandle( + handle: recordTester.Handle.DangerousGetHandle(), + ownsHandle: false, + gtype: TypedRecordTester.GetGType() + ); + + wrapped.Handle.DangerousGetHandle().Should().Be(recordTester.Handle.DangerousGetHandle()); + recordTester.GetRefCount().Should().Be(2); + wrapped.GetRefCount().Should().Be(2); + } + + [TestMethod] + public void SupportsCallbackWithOutParameter() + { + var instance = TypedRecordTester.New(); + + void Callback(out TypedRecordTester? recordTester) + { + recordTester = instance; + } + + var result = TypedRecordTester.RunCallbackCreateNullableFullOwnershipTransferOut(Callback); + result!.Handle.DangerousGetHandle().Should().Be(instance.Handle.DangerousGetHandle()); + } +}