diff --git a/GirCore.sln b/GirCore.sln index 57cfd05c0..193e94067 100644 --- a/GirCore.sln +++ b/GirCore.sln @@ -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 @@ -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} @@ -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 diff --git a/Libs/GObject/Classes/Object.Properties.cs b/Libs/GObject/Classes/Object.Properties.cs index 6fdbe605b..855c1ded2 100644 --- a/Libs/GObject/Classes/Object.Properties.cs +++ b/Libs/GObject/Classes/Object.Properties.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; namespace GObject { @@ -67,6 +68,41 @@ protected Value GetProperty(string? name) return value; } + protected static void InstallProperty(uint id, IntPtr objectClass, Property property) + { + var spec = GetParamSpec(property); + ObjectClass.Native.install_property(objectClass, id, spec); + } + + private static IntPtr GetParamSpec(Property 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(Property 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 } } diff --git a/Libs/GObject/Classes/Object.TypeHelper.cs b/Libs/GObject/Classes/Object.TypeHelper.cs index 911af87d7..d8269a0fc 100644 --- a/Libs/GObject/Classes/Object.TypeHelper.cs +++ b/Libs/GObject/Classes/Object.TypeHelper.cs @@ -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, diff --git a/Libs/GObject/Classes/Object.TypeIntegration.cs b/Libs/GObject/Classes/Object.TypeIntegration.cs index 0c251e061..93b5c88fc 100644 --- a/Libs/GObject/Classes/Object.TypeIntegration.cs +++ b/Libs/GObject/Classes/Object.TypeIntegration.cs @@ -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) diff --git a/Libs/GObject/Classes/Object.cs b/Libs/GObject/Classes/Object.cs index 9a6426e21..0c4b5124d 100644 --- a/Libs/GObject/Classes/Object.cs +++ b/Libs/GObject/Classes/Object.cs @@ -358,6 +358,27 @@ public static bool TryWrapHandle(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 diff --git a/Libs/GObject/Classes/Property.cs b/Libs/GObject/Classes/Property.cs index 02118add6..858308c35 100644 --- a/Libs/GObject/Classes/Property.cs +++ b/Libs/GObject/Classes/Property.cs @@ -20,6 +20,16 @@ public sealed class Property /// private Action? _set; + /// + /// The type getter + /// + private Func? _getType; + + /// + /// Property kind. + /// + private Types _kind; + #endregion #region Properties @@ -45,6 +55,8 @@ public sealed class Property /// public bool IsWriteable => _set != null; + public Types Kind => _kind; + #endregion #region Constructors @@ -68,6 +80,8 @@ private Property(string name, string propertyName) #region Methods + public Type? GetGType() => _getType?.Invoke(); + /// /// Registers this property descriptor and creates the correct GProperty /// into the GLib type of . @@ -76,17 +90,21 @@ private Property(string name, string propertyName) /// The name of the C# property which serves as the proxy of this GProperty /// The function called when retrieving the value of this property in bindings. /// The function called when defining the value of this property in bindings. + /// The function called to get the type of this property + /// The kind of this property /// The type of the object on which this property will be registered. /// /// An instance of representing the GProperty description. /// - public static Property Register(string name, string propertyName, Func? get = null, Action? set = null) + public static Property Register(string name, string propertyName, Func? get = null, Action? set = null, Func? getType = null, Types kind = Types.Invalid) where TObject : Object { return new Property(name, propertyName) { _get = get is null ? null : new Func((o) => get((TObject) o)), _set = set is null ? null : new Action((o, v) => set((TObject) o, v)), + _getType = getType, + _kind = kind }; } @@ -98,17 +116,21 @@ public static Property Register(string name, string propertyName, Fu /// The name of the C# property which serves as the proxy of this GProperty /// The function called when retrieving the value of this property in bindings. /// The function called when defining the value of this property in bindings. + /// The function called to get the type of this property + /// The kind of this property /// The type of the object on which this property will be wrapped. /// /// An instance of representing the GProperty description. /// - public static Property Wrap(string name, string propertyName, Func? get = null, Action? set = null) + public static Property Wrap(string name, string propertyName, Func? get = null, Action? set = null, Func? getType = null, Types kind = Types.Invalid) where TObject : IObject { return new Property(name, propertyName) { _get = get is null ? null : new Func((o) => get((TObject) o)), _set = set is null ? null : new Action((o, v) => set((TObject) o, v)), + _getType = getType, + _kind = kind }; } diff --git a/Libs/GObject/Structs/InterfaceInfo.cs b/Libs/GObject/Structs/InterfaceInfo.cs new file mode 100644 index 000000000..d43b49c3c --- /dev/null +++ b/Libs/GObject/Structs/InterfaceInfo.cs @@ -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; + } + } +} diff --git a/Libs/GObject/Structs/Type.cs b/Libs/GObject/Structs/Type.cs index 6e9a095ec..e3d31465e 100644 --- a/Libs/GObject/Structs/Type.cs +++ b/Libs/GObject/Structs/Type.cs @@ -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, diff --git a/Libs/GObject/Structs/Value.cs b/Libs/GObject/Structs/Value.cs index 35e05851f..899719a4c 100644 --- a/Libs/GObject/Structs/Value.cs +++ b/Libs/GObject/Structs/Value.cs @@ -117,6 +117,11 @@ public string GetString() return Marshal.PtrToStringAnsi(ptr) ?? throw new Exception("Could not get string value"); } + public void SetEnum(T value) where T : Enum + { + Native.set_enum(ref this, Convert.ToInt32(value)); + } + #endregion #region IDisposable Implementation diff --git a/Libs/Gtk3/Classes/EnumHelper.cs b/Libs/Gtk3/Classes/EnumHelper.cs new file mode 100644 index 000000000..119555d2c --- /dev/null +++ b/Libs/Gtk3/Classes/EnumHelper.cs @@ -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(); + } + } +} diff --git a/Samples/Gtk3/Template/CompositeWidget.cs b/Samples/Gtk3/Template/CompositeWidget.cs index d596c55fa..bee5b05c4 100644 --- a/Samples/Gtk3/Template/CompositeWidget.cs +++ b/Samples/Gtk3/Template/CompositeWidget.cs @@ -1,14 +1,24 @@ 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, @@ -16,6 +26,45 @@ private static void ClassInit(Type gClass, System.Type type, IntPtr classData) ); BindTemplateChild(gClass, nameof(Button)); BindTemplateSignals(gClass, type); + + unsafe + { + var myClass = (ObjectClass*) gclass; + + var setProp = Marshal.GetFunctionPointerForDelegate(MySetProperty); + var getProp = Marshal.GetFunctionPointerForDelegate(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(handle); + + if (propertyId == 1) + obj.Orientation = value.Extract(); + } + + private static void MyGetProperty(IntPtr handle, uint propertyId, ref Value value, IntPtr paramSpec) + { + var obj = WrapHandle(handle); + + if (propertyId == 1) + value.SetEnum(obj.Orientation); + } + + private static void RegisterInterfaces(Type gClass) + { + RegisterInterface(gClass, Gtk.Orientable.GTypeDescriptor.GType); } protected override void Initialize() diff --git a/Samples/Gtk3/Template/Program.cs b/Samples/Gtk3/Template/Program.cs index 10d0744b7..a691d28a2 100644 --- a/Samples/Gtk3/Template/Program.cs +++ b/Samples/Gtk3/Template/Program.cs @@ -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 OrientationProperty = Property.Wrap( + 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() }; diff --git a/Samples/Gtk3/Template/Template.csproj b/Samples/Gtk3/Template/Template.csproj index a7d1a0215..eb9155dc9 100644 --- a/Samples/Gtk3/Template/Template.csproj +++ b/Samples/Gtk3/Template/Template.csproj @@ -8,6 +8,7 @@ Exe net5.0 enable + true