Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spike/interfacesupport1 #196

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions GirCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gst.Pbutils", "Libs\Gst.Pbu
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gst.Base", "Libs\Gst.Base\Gst.Base.csproj", "{8678914B-C262-4EFB-BCC0-96A15D969C2B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Template", "Samples\Gtk3\Template\Template.csproj", "{8AC8104E-0F77-466C-83A3-B67D225DFE3D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -288,6 +290,18 @@ Global
{8678914B-C262-4EFB-BCC0-96A15D969C2B}.Release|x64.Build.0 = Release|Any CPU
{8678914B-C262-4EFB-BCC0-96A15D969C2B}.Release|x86.ActiveCfg = Release|Any CPU
{8678914B-C262-4EFB-BCC0-96A15D969C2B}.Release|x86.Build.0 = Release|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Debug|x64.ActiveCfg = Debug|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Debug|x64.Build.0 = Debug|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Debug|x86.ActiveCfg = Debug|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Debug|x86.Build.0 = Debug|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Release|Any CPU.Build.0 = Release|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Release|x64.ActiveCfg = Release|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Release|x64.Build.0 = Release|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Release|x86.ActiveCfg = Release|Any CPU
{8AC8104E-0F77-466C-83A3-B67D225DFE3D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BF7F9B0B-CB43-4161-BFAD-C6EE479FC86B} = {386AE10F-B7AC-4C97-AC5C-202D3662A868}
Expand All @@ -308,5 +322,6 @@ Global
{E22AC3F0-0298-4784-9C91-76138FA3BD74} = {386AE10F-B7AC-4C97-AC5C-202D3662A868}
{0488271F-63F9-41BD-A38B-0FD0D7616BE3} = {386AE10F-B7AC-4C97-AC5C-202D3662A868}
{8678914B-C262-4EFB-BCC0-96A15D969C2B} = {386AE10F-B7AC-4C97-AC5C-202D3662A868}
{8AC8104E-0F77-466C-83A3-B67D225DFE3D} = {79FE88D9-9545-4AE8-80AF-8E2E2E55E8A3}
EndGlobalSection
EndGlobal
36 changes: 36 additions & 0 deletions Libs/GObject/Classes/Object.Properties.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;

namespace GObject
{
Expand Down Expand Up @@ -67,6 +68,41 @@ protected Value GetProperty(string? name)
return value;
}

protected static void InstallProperty<T>(uint id, IntPtr objectClass, Property<T> property)
{
var spec = GetParamSpec(property);
ObjectClass.Native.install_property(objectClass, id, spec);
}

private static IntPtr GetParamSpec<T>(Property<T> property)
{
var name = property.Name;
var nick = property.PropertyName;
var blurb = "The " + nick + " property";

ParamFlags flags = default;

if (property.IsReadable)
flags |= ParamFlags.Readable;
if (property.IsWriteable)
flags |= ParamFlags.Writable;

return property.Kind switch
{
Types.Enum => Global.Native.param_spec_enum(name, nick, blurb, GetType(property), 0, flags),
_ => throw new NotSupportedException("Unknown property type")
};
}

private static ulong GetType<T>(Property<T> property)
{
Type? type = property.GetGType();
if (type is null)
throw new Exception($"Can not register property {property.Name}. Type is not specified");

return type.Value.Value;
}

#endregion
}
}
4 changes: 2 additions & 2 deletions Libs/GObject/Classes/Object.TypeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ internal static ClassInitFunc GetClassInitFunc(IReflect type) => (gClass, classD
InvokeStaticMethod(
type: type,
name: "ClassInit",
parameters: new object[] {gtype, type, classData}
parameters: new object[] {gtype, type, gClass, classData}
);
};

private static void InvokeStaticMethod(IReflect type, string name, params object[] parameters)
public static void InvokeStaticMethod(IReflect type, string name, params object[] parameters)
{
MethodInfo? method = type.GetMethod(
name: name,
Expand Down
9 changes: 8 additions & 1 deletion Libs/GObject/Classes/Object.TypeIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,15 @@ private static void RegisterNativeType(System.Type type)
// Free Memory
Marshal.FreeHGlobal(ptr);

var newType = new Type(typeid);
// Register type in type dictionary
TypeDictionary.AddSingle(type, new Type(typeid));
TypeDictionary.AddSingle(type, newType);

TypeHelper.InvokeStaticMethod(
type: type,
name: "RegisterInterfaces",
parameters: newType
);
}

private static Type TypeFromHandle(IntPtr handle)
Expand Down
21 changes: 21 additions & 0 deletions Libs/GObject/Classes/Object.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,27 @@ public static bool TryWrapHandle<T>(IntPtr handle, [NotNullWhen(true)] out T? o)
}
}

protected static void RegisterInterface(Type gClass, Type interfaceType)
{
var interfaceStruct = new InterfaceInfo(
initFunc: InterfaceInit);
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(interfaceStruct));
try
{
Marshal.StructureToPtr(interfaceStruct, ptr, true);
Global.Native.type_add_interface_static(gClass.Value, interfaceType.Value, ptr);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}

private static void InterfaceInit(IntPtr g_iface, IntPtr iface_data)
{

}

#endregion

#region IDisposable Implementation
Expand Down
26 changes: 24 additions & 2 deletions Libs/GObject/Classes/Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ public sealed class Property<T>
/// </summary>
private Action<IObject, T>? _set;

/// <summary>
/// The type getter
/// </summary>
private Func<Type>? _getType;

/// <summary>
/// Property kind.
/// </summary>
private Types _kind;

#endregion

#region Properties
Expand All @@ -45,6 +55,8 @@ public sealed class Property<T>
/// </summary>
public bool IsWriteable => _set != null;

public Types Kind => _kind;

#endregion

#region Constructors
Expand All @@ -68,6 +80,8 @@ private Property(string name, string propertyName)

#region Methods

public Type? GetGType() => _getType?.Invoke();

/// <summary>
/// Registers this property descriptor and creates the correct GProperty
/// into the GLib type of <typeparamref name="TObject"/>.
Expand All @@ -76,17 +90,21 @@ private Property(string name, string propertyName)
/// <param name="propertyName">The name of the C# property which serves as the proxy of this GProperty</param>
/// <param name="get">The function called when retrieving the value of this property in bindings.</param>
/// <param name="set">The function called when defining the value of this property in bindings.</param>
/// <param name="getType">The function called to get the type of this property</param>
/// <param name="kind">The kind of this property</param>
/// <typeparam name="TObject">The type of the object on which this property will be registered.</typeparam>
/// <returns>
/// An instance of <see cref="Property{T}"/> representing the GProperty description.
/// </returns>
public static Property<T> Register<TObject>(string name, string propertyName, Func<TObject, T>? get = null, Action<TObject, T>? set = null)
public static Property<T> Register<TObject>(string name, string propertyName, Func<TObject, T>? get = null, Action<TObject, T>? set = null, Func<Type>? getType = null, Types kind = Types.Invalid)
where TObject : Object
{
return new Property<T>(name, propertyName)
{
_get = get is null ? null : new Func<IObject, T>((o) => get((TObject) o)),
_set = set is null ? null : new Action<IObject, T>((o, v) => set((TObject) o, v)),
_getType = getType,
_kind = kind
};
}

Expand All @@ -98,17 +116,21 @@ public static Property<T> Register<TObject>(string name, string propertyName, Fu
/// <param name="propertyName">The name of the C# property which serves as the proxy of this GProperty</param>
/// <param name="get">The function called when retrieving the value of this property in bindings.</param>
/// <param name="set">The function called when defining the value of this property in bindings.</param>
/// <param name="getType">The function called to get the type of this property</param>
/// <param name="kind">The kind of this property</param>
/// <typeparam name="TObject">The type of the object on which this property will be wrapped.</typeparam>
/// <returns>
/// An instance of <see cref="Property{T}"/> representing the GProperty description.
/// </returns>
public static Property<T> Wrap<TObject>(string name, string propertyName, Func<TObject, T>? get = null, Action<TObject, T>? set = null)
public static Property<T> Wrap<TObject>(string name, string propertyName, Func<TObject, T>? get = null, Action<TObject, T>? set = null, Func<Type>? getType = null, Types kind = Types.Invalid)
where TObject : IObject
{
return new Property<T>(name, propertyName)
{
_get = get is null ? null : new Func<IObject, T>((o) => get((TObject) o)),
_set = set is null ? null : new Action<IObject, T>((o, v) => set((TObject) o, v)),
_getType = getType,
_kind = kind
};
}

Expand Down
14 changes: 14 additions & 0 deletions Libs/GObject/Structs/InterfaceInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace GObject
{
public partial struct InterfaceInfo
{
public InterfaceInfo(InterfaceInitFunc? initFunc = null, InterfaceFinalizeFunc? finalizeFunc = null)
{
this.interface_init = initFunc!;
this.interface_finalize = finalizeFunc!;
interface_data = IntPtr.Zero;
}
}
}
2 changes: 1 addition & 1 deletion Libs/GObject/Structs/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public override string ToString()
//Offsets see: https://gitlab.gnome.org/GNOME/glib/blob/master/gobject/gtype.h
}

internal enum Types
public enum Types
{
Invalid = 0 << 2,
None = 1 << 2,
Expand Down
5 changes: 5 additions & 0 deletions Libs/GObject/Structs/Value.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ public string GetString()
return Marshal.PtrToStringAnsi(ptr) ?? throw new Exception("Could not get string value");
}

public void SetEnum<T>(T value) where T : Enum
{
Native.set_enum(ref this, Convert.ToInt32(value));
}

#endregion

#region IDisposable Implementation
Expand Down
18 changes: 18 additions & 0 deletions Libs/Gtk3/Classes/EnumHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Runtime.InteropServices;
using GObject;

namespace Gtk
{
//TODO: Generate this automatically
public static class EnumHelper
{
public static Type GetOrientationType()
=> new Type(Native.gtk_orientation_get_type());

private static class Native
{
[DllImport("Gtk", EntryPoint = "gtk_orientation_get_type")]
public static extern ulong gtk_orientation_get_type();
}
}
}
53 changes: 51 additions & 2 deletions Samples/Gtk3/Template/CompositeWidget.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,70 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using GObject;
using Gtk;
using Type = GObject.Type;

namespace GtkDemo
{
public class CompositeWidget : Bin
public class CompositeWidget : Bin, Orientable
{
private static void ClassInit(Type gClass, System.Type type, IntPtr classData)
public Orientation Orientation { get; set; }

//Just for Demo purpose
public Orientation OrientationInterfaceAccess
{
get => GetProperty(Orientable.OrientationProperty);
set => SetProperty(Orientable.OrientationProperty, value);
}

private static void ClassInit(Type gClass, System.Type type, IntPtr gclass, IntPtr classData)
{
SetTemplate(
gtype: gClass,
template: Assembly.GetExecutingAssembly().ReadResource("CompositeWidget.ui")
);
BindTemplateChild(gClass, nameof(Button));
BindTemplateSignals(gClass, type);

unsafe
{
var myClass = (ObjectClass*) gclass;

var setProp = Marshal.GetFunctionPointerForDelegate<PropAccess>(MySetProperty);
var getProp = Marshal.GetFunctionPointerForDelegate<PropAccess>(MyGetProperty);
myClass->set_property = setProp;
myClass->get_property = getProp;

InstallProperty(
id: 1,
objectClass: gclass,
property: Orientable.OrientationProperty
);
}
}

private delegate void PropAccess(IntPtr handle, uint propertyId, ref Value value, IntPtr paramSpec);

private static void MySetProperty(IntPtr handle, uint propertyId, ref Value value, IntPtr paramSpec)
{
var obj = WrapHandle<CompositeWidget>(handle);

if (propertyId == 1)
obj.Orientation = value.Extract<Orientation>();
}

private static void MyGetProperty(IntPtr handle, uint propertyId, ref Value value, IntPtr paramSpec)
{
var obj = WrapHandle<CompositeWidget>(handle);

if (propertyId == 1)
value.SetEnum(obj.Orientation);
}

private static void RegisterInterfaces(Type gClass)
{
RegisterInterface(gClass, Gtk.Orientable.GTypeDescriptor.GType);
}

protected override void Initialize()
Expand Down
22 changes: 21 additions & 1 deletion Samples/Gtk3/Template/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,32 @@ public static class Program
public static void Main(string[] args)
{
Global.Init();
var c = new CompositeWidget();

/* In the generated code the orientable property needs to be defined like this:

Some generated visiblity must probably be changed manually be adopted to be visible. I did not update the generator.

public static readonly Property<Orientation> OrientationProperty = Property<Orientation>.Wrap<Orientable>(
Native.OrientationProperty,
nameof(Orientation),
(o) => o.Orientation,
(o, v) => o.Orientation = v,
EnumHelper.GetOrientationType,
Types.Enum
);
*/

c.Orientation = Orientation.Vertical; //Write Managed data
Console.WriteLine(c.OrientationInterfaceAccess); //Read via C world
c.OrientationInterfaceAccess = Orientation.Horizontal; //Write via C world
Console.WriteLine(c.Orientation); //Read managed data

var mainWindow = new Window("MyWindow")
{
DefaultWidth = 300,
DefaultHeight = 200,
Child = new CompositeWidget(),
Child = c,
[Window.DestroySignal] = (o, e) => Global.MainQuit()
};

Expand Down
1 change: 1 addition & 0 deletions Samples/Gtk3/Template/Template.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down